这个题目,只要想到离线,然后先建一颗树,树链剖分一下,logn的时间维护一段路径,就可以了。
#include<bits/stdc++.h> using namespace std; const int maxn = 30000 + 10; const int maxm = 100000 + 10; int n,m,Q; struct Edge { int from,to; Edge(){} Edge(int a,int b):from(a),to(b){} bool operator < (const Edge &A) const { return from == A.from ? to < A.to : from < A.from; } }edge; struct Qu { int op,u,v; }q[maxm]; multiset<Edge>s; vector<int>G[maxn]; int Fa[maxn],Rank[maxn]; int find(int x) { if(x!=Fa[x]) Fa[x] = find(Fa[x]); return Fa[x]; } void unio(int a,int b){ int fx = find(a),fy = find(b); if( Rank[fx] > Rank[fy] ){ Fa[ fy ] = fx; Rank[fx] ++; } else{ Fa[ fx ] = fy; Rank[fy] ++ ; } } ///线段树部分 struct Segment { #define rt get_id(l,r) #define ls get_id(l,mid) #define rs get_id(mid+1,r) int get_id(int l,int r){ return l + r | l != r; } int sum[maxn<<1],root[maxn<<1]; void up(int l,int r){ int mid = l + r >> 1; sum[rt] = sum[ls] + sum[rs]; } void down(int l,int r){ int mid = l + r >> 1; if(root[rt]){ root[ls] = root[rs] = root[rt]; sum[ls] = sum[rs] = 0; root[rt] = 0; } } void build(int l,int r){ root[rt] = 0; if( l == r){ sum[rt] = 1; return ; } int mid = l + r >> 1; build(l,mid); build(mid+1,r); up(l,r); } void update(int l,int r,int L,int R) { if(L<=l && R>=r){ sum[rt] = 0; root[rt] = 1; return ; } int mid = l + r >> 1; down(l,r); if(L<=mid) update(l,mid,L,R); if(R>mid) update(mid+1,r,L,R); up(l,r); } int query(int l,int r,int L,int R) { if(L<=l && R>=r){ return sum[rt]; } down(l,r); int mid = l + r >> 1,res = 0; if(L<=mid) res += query(l,mid,L,R); if(R>mid) res += query(mid+1,r,L,R); return res; } }seg; /// ///树剖部分 int tree[maxn]; int z,root; int dep[maxn], w[maxn], fa[maxn], top[maxn], son[maxn], siz[maxn]; void dfs(int v) { siz[v] = 1; son[v] = 0; for (int i = 0; i < G[v].size(); i ++ ) if (G[v][i] != fa[v]) { fa[ G[v][i] ] = v; dep[ G[v][i] ] = dep[v]+1; dfs( G[v][i] ); if (siz[ G[v][i] ] > siz[son[v]]) son[v] = G[v][i]; siz[v] += siz[ G[v][i] ]; } } void build_tree(int v, int tp) { w[v] = ++ z; top[v] = tp; if (son[v] != 0) build_tree(son[v], top[v]); for (int i = 0; i < G[v].size(); i ++) if (G[v][i] != son[v] && G[v][i] != fa[v]) build_tree(G[v][i], G[v][i]); } void init() { root = 1; fa[root] = z = dep[root] = 0; memset(siz, 0, sizeof(siz)); memset(tree, 0, sizeof(tree)); dfs(root); build_tree(root,root); } inline void find(int va, int vb) { int f1 = top[va], f2 = top[vb]; while (f1 != f2){ if (dep[f1] < dep[f2]){ swap(f1, f2); swap(va, vb); } seg.update(1,z,w[f1],w[va]); va = fa[f1]; f1 = top[va]; } if (va == vb) return ; if (dep[va] > dep[vb]) swap(va, vb); seg.update(1,z,w[ son[va] ],w[vb]); return ; } inline int query(int va, int vb) { int f1 = top[va], f2 = top[vb], tmp = 0; while (f1 != f2){ if (dep[f1] < dep[f2]){ swap(f1, f2); swap(va, vb); } tmp += seg.query(1,z,w[f1],w[va]); va = fa[f1]; f1 = top[va]; } if (va == vb) return tmp; if (dep[va] > dep[vb]) swap(va, vb); tmp += seg.query(1,z,w[ son[va] ],w[vb]); return tmp; } /// int ans[maxm]; bool vis[maxm]; int main() { int T,cas=0;scanf("%d",&T); while(T--){ scanf("%d%d%d",&n,&m,&Q); s.clear(); for(int i=1;i<=m;i++) { scanf("%d%d",&edge.from,&edge.to); if(edge.from > edge.to) swap(edge.from,edge.to); s.insert(edge); } for(int i=1;i<=n;i++) G[i].clear(),Fa[i] = i,Rank[i] = 1; for(int i=1;i<=Q;i++) { scanf("%d%d%d",&q[i].op,&q[i].u,&q[i].v); if(q[i].u > q[i].v) swap(q[i].u,q[i].v); if(q[i].op == 1){ s.erase(s.find(Edge(q[i].u,q[i].v))); } } int id = 0; memset(vis,0,sizeof(vis)); for(multiset<Edge>::iterator it = s.begin();it!=s.end();it++) { id ++ ; Edge k = (*it); if(find(k.from) == find(k.to)) continue; G[k.from].push_back(k.to); G[k.to].push_back(k.from); unio(k.from,k.to); vis[ id ] = 1; } init(); seg.build(1,z); id = 0; for(multiset<Edge>::iterator it = s.begin();it!=s.end();it++) { id++; Edge k = (*it); if(vis[id]) continue; int a = k.from,b = k.to; find(a,b); } for(int i=Q;i>=1;i--) if(q[i].op == 2){ ans[i] = query(q[i].u,q[i].v); } else{ find(q[i].u,q[i].v); } printf("Case #%d:\n",++cas); for(int i=1;i<=Q;i++) { if(q[i].op == 2) printf("%d\n",ans[i]); } } }