我们考虑离线,把第三种操作挂在第二种上,那么我们只需要查询对应时刻的森林中某个点是不是当前操作点的祖先即可
因为只有加边操作,我们把森林的祖先后代关系变成判断他在离线的最终树上的祖先后代关系和判断两点是否连通
并查集维护连通性,LCA查询祖先后代关系
复杂度(O(m log n))
1 #include<bits/stdc++.h> 2 #define maxn 100005 3 using namespace std; 4 int n,m; 5 struct Opt 6 { 7 int op,x,y; 8 Opt(int OP=0,int X=0,int Y=0):op(OP),x(X),y(Y){} 9 }opt[maxn]; 10 vector< pair<int,int> > qry[maxn]; 11 int Ans[maxn]; 12 int m1,m2; 13 int fa[maxn]; 14 int find(int x){if(fa[x]==x)return x;else return fa[x]=find(fa[x]);} 15 vector<int> g[maxn]; 16 bool isroot[maxn]; 17 int anc[maxn][20],d[maxn]; 18 void dfs(int u) 19 { 20 for(int v:g[u]) 21 { 22 d[v]=d[u]+1;anc[v][0]=u; 23 dfs(v); 24 } 25 } 26 int LCA(int u,int v) 27 { 28 if(d[u]<=d[v])swap(u,v); 29 for(int i=18;i>=0;--i)if(d[anc[u][i]]>=d[v])u=anc[u][i]; 30 if(u==v)return u; 31 for(int i=18;i>=0;--i)if(anc[u][i]!=anc[v][i])u=anc[u][i],v=anc[v][i]; 32 return anc[u][0]; 33 } 34 int main() 35 { 36 scanf("%d%d",&n,&m); 37 for(int i=1;i<=n;++i)fa[i]=i,isroot[i]=1; 38 for(int i=1;i<=m;++i) 39 { 40 int t,x,y; 41 scanf("%d%d",&t,&x); 42 if(t!=2)scanf("%d",&y); 43 if(t==1)opt[++m1]=Opt(t,x,y),g[y].push_back(x),isroot[x]=0; 44 if(t==2)opt[++m1]=Opt(t,x); 45 if(t==3)qry[y].push_back(make_pair(x,++m2)); 46 } 47 for(int i=1;i<=n;++i)if(isroot[i])g[0].push_back(i); 48 dfs(0); 49 for(int j=1;j<=18;++j) 50 for(int i=1;i<=n;++i)anc[i][j]=anc[anc[i][j-1]][j-1]; 51 int id=0; 52 for(int i=1;i<=m1;++i) 53 { 54 int t,x,y; 55 t=opt[i].op,x=opt[i].x,y=opt[i].y; 56 if(t==1) 57 { 58 if(find(x)!=find(y))fa[find(x)]=find(y); 59 } 60 if(t==2) 61 { 62 ++id; 63 for(auto pa:qry[id]) 64 { 65 int u=x,v=pa.first; 66 if(LCA(u,v)==v&&find(u)==find(v))Ans[pa.second]=1; 67 else Ans[pa.second]=0; 68 } 69 } 70 } 71 for(int i=1;i<=m2;++i)if(Ans[i])puts("YES");else puts("NO"); 72 }