#include using namespace std; #define ALL(x) x.begin(),x.end() #define rep(i,n) for(int i=0;i<(n);i++) #define debug(v) cout<<#v<<":";for(auto x:v){cout<bool chmax(T &a,const T &b){if(abool chmin(T &a,const T &b){if(b ostream &operator<<(ostream &os,const pair&p){ os< ostream &operator<<(ostream &os,const vector&v){ for(int i=0;i<(int)v.size();i++) os< istream &operator>>(istream &is,pair&p){ is>>p.first>>p.second; return is; } template istream &operator>>(istream &is,vector&v){ for(T &x:v)is>>x; return is; } template struct rerooting{ using F=function;//集合,頂点番号 using M=function; int V; vector> G; vector> dp; vector ans; // dp_v = g(merge(f(dp_c1,c1),...,f(dp_ck,ck)),v) F f,g; // TxN->T M merge;// TxT->T,子を集約する関数,モノイド T gen; //gen: mergeの元 rerooting(int V,F f,M merge,T gen,F g=[](T a,int b){return a;}) :V(V),f(f),merge(merge),gen(gen),g(g),G(V),dp(V),ans(V,gen){} //bidirectional void add_edge(int a,int b){ G[a].push_back(b); G[b].push_back(a); } T dfs1(int pre,int now){ T ret=gen; for(int i=0;i lsum(G[now].size()+1),rsum(G[now].size()+1);//親も混ぜて累積 lsum[0]=gen;rsum[G[now].size()]=gen; for(int i=0;i0;i--) rsum[i-1]=merge(rsum[i],f(dp[now][i-1],G[now][i-1])); for(int i=0;i par,siz; int con; UnionFind(int n){ par.resize(n); iota(begin(par),end(par),0); siz.assign(n,1); } int root(int x){ return (par[x]==x?x:(par[x]=root(par[x]))); } bool sameroot(int x,int y){ return root(x)==root(y); } bool unite(int x,int y){ x=root(x);y=root(y); if(x==y) return false; if(siz[x]> graph; struct DoublingLowestCommonAncestor{ const int LOG; vector dep; const graph &g; //table[i][j]は点jの2^i個上の親情報 vector> table; DoublingLowestCommonAncestor(const graph &g):g(g),dep(g.size()),LOG(32-__builtin_clz(g.size())){ table.assign(LOG,vector(g.size(),-1)); } void dfs(int idx,int par,int d){ //1個上の親の記録 table[0][idx]=par; dep[idx]=d; for(auto &to:g[idx]){ if(to!=par) dfs(to,idx,d+1); } } //根としたい点をroot void build(int root){ dfs(root,-1,0); for(int k=0;k+1dep[v]として進める if(dep[u]>dep[v]) swap(u,v); //深さをそろえる for(int i=LOG-1;i>=0;i--){ if(((dep[v]-dep[u])>>i)&1) v=table[i][v]; } if(u==v) return u; for(int i=LOG-1;i>=0;i--){ if(table[i][u]!=table[i][v]){ u=table[i][u]; v=table[i][v]; } } return table[0][u]; } //呼び出すたびO(logn) int dis(int u,int v){ int p=query(u,v); return dep[u]+dep[v]-2*dep[p]; } //いるか?これ int operator[](const int &k) const{ return dep[k]; } //頂点xのk個上の親を求める。無ければ-1,未verify int parent(int x,int k){ int ret=x; int bit=0; for(int i=LOG-1;i>=0 and ret>=0;i--){ if((k>>i)&1) ret=table[i][ret]; } return ret; } }; signed main(){ int n,m,q;cin>>n>>m>>q; UnionFind uf(n); using P=pair; vector

es; rep(i,m){ int u,v;cin>>u>>v;u--,v--; es.push_back(P(u,v)); uf.unite(u,v); } vector> ce(n); vector> cv(n); for(auto [u,v]:es){ ce[uf.root(u)].push_back(P(u,v)); } rep(i,n)cv[uf.root(i)].push_back(i); vector> qe(n); vector cnt(n,0); rep(i,q){ int u,v;cin>>u>>v;u--,v--; if(uf.sameroot(u,v))qe[uf.root(u)].push_back(P(u,v)); else cnt[u]++,cnt[v]++; } int res=0; rep(i,n){ if(cv[i].empty())continue; map id; int sz=0; for(auto x:cv[i])id[x]=sz++; vector> g(sz); for(auto [u,v]:ce[i]){ g[id[u]].push_back(id[v]); g[id[v]].push_back(id[u]); } vector a(sz); for(auto x:cv[i])a[id[x]]=cnt[x]; DoublingLowestCommonAncestor lca(g); lca.build(0); for(auto [u,v]:qe[i]){ res+=lca.dis(id[u],id[v]); } auto f=[&](P fue,int v){return make_pair(fue.first+fue.second,fue.second);}; auto merge=[&](P lhs,P rhs){return make_pair(lhs.first+rhs.first,rhs.second+lhs.second);}; auto gf=[&](P fue,int v){return make_pair(fue.first,fue.second+a[v]);}; rerooting

re(sz,f,merge,make_pair(0,0),gf); re.build(); int mugen=INF; rep(j,sz){ chmin(mugen,re.ans[j].first); } debug(re.ans); debug(a); res+=mugen; debug(cv[i]); cout<