一、题目
二、解法
发现树的形态一直改变维护着是很难受的,可以考虑把它离线下来。可以用并查集来维护,操作 1 1 1直接连边,操作 2 2 2把某个点到他现在的根的路径存下来,这条路径一定是到根的一条链。
对于每个询问,就相当于是看一个点是不是在一条到根的路径上,把这个点和开始点求 l c a lca lca,判断是不是开始点的祖先,再把这个点和终点求 l c a lca lca,判断是不是终点是不是这个点的祖先,两个条件同时成立即可。注意要判断当前点和路径不在一个连通块内的情况。
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 100005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,q,tot,a[M],b[M],c[M],d[M],f[M];
int dep[M],p[M][20],fa[M];
struct edge
{
int v,next;
}e[2*M];
int find(int x)
{
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void merge(int u,int v)
{
e[++tot]=edge{v,f[u]},f[u]=tot;
e[++tot]=edge{u,f[v]},f[v]=tot;
int x=find(u),y=find(v);
fa[x]=y;
}
void dfs(int u)
{
dep[u]=dep[p[u][0]]+1;
for(int i=1;i<20;i++)
p[u][i]=p[p[u][i-1]][i-1];
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==p[u][0]) continue;
p[v][0]=u;
dfs(v);
}
}
int lca(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
for(int i=19;i>=0;i--)
if(dep[p[u][i]]>=dep[v])
u=p[u][i];
if(u==v) return u;
for(int i=19;i>=0;i--)
if(p[u][i]^p[v][i])
u=p[u][i],v=p[v][i];
return p[u][0];
}
signed main()
{
n=read();m=read();
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++)
{
int op=read(),x=read();
if(op==1)
merge(x,read());
if(op==2)
a[++k]=x,b[k]=find(x);
if(op==3)
c[++q]=x,d[q]=read();
}
for(int i=1;i<=n;i++)
if(i==find(i))
dfs(i);
for(int i=1;i<=q;i++)
{
int t=lca(c[i],a[d[i]]),t2=lca(c[i],b[d[i]]);
if(find(c[i])!=find(a[d[i]]))
{
puts("NO");
continue;
}
if(t==c[i] && t2==b[d[i]]) puts("YES");
else puts("NO");
}
}