由于离线,考虑一个离线做法。对于这种一条边在某一区间出现的题目,往往可以用线段树分治离线解决。
先建立时间线段树,然后将每条边按照出现的时间插入到结点上面,然后 dfs 整棵线段树,走到某一个节点就对当前节点的所有边在并查集中进行 link 操作,然后递归两边,离开这个节点的时候把 link 操作撤销。
这样我们的并查集只需要支持撤销最后一个操作而不是删除某一条边,只要在 link 的时候记录一下连了哪条边即可。
值得注意的是并查集不能使用路径压缩,因为这样撤销操作可以退化成 \(O(n^2)\),无法接受,应当使用按秩合并并查集。
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
struct element
{
int opt,x,y;
bool operator <(const element &other) const
{
return opt<other.opt;
}
}q[500001];
int n,m,vis[5001][5001],bin[5001],s[5001],root;
int tot,ls[500001<<1],rs[500001<<1];
vector<element> v[500001<<1];
vector<pair<int,int> > tmp[500001<<1];
inline int read()
{
int x=0;
char c=getchar();
while(c<‘0‘||c>‘9‘)
c=getchar();
while(c>=‘0‘&&c<=‘9‘)
{
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x;
}
int anc(int k)
{
if(!bin[k])
return k;
return anc(bin[k]);
}
inline pair<int,int> link(int x,int y)
{
x=anc(x);
y=anc(y);
if(x==y)
return make_pair(0,0);
if(s[x]<s[y])
x^=y^=x^=y;
bin[y]=x;
s[x]+=s[y];
return make_pair(x,y);
}
inline void del(int x,int y)
{
bin[y]=0;
s[x]-=s[y];
}
void update(int nl,int nr,int l,int r,int &k,element p)
{
if(!k)
k=++tot;
if(l>=nl&&r<=nr)
{
v[k].push_back(p);
return;
}
int mid=(l+r)>>1;
if(nl<=mid)
update(nl,nr,l,mid,ls[k],p);
if(nr>mid)
update(nl,nr,mid+1,r,rs[k],p);
}
void query(int l,int r,int k)
{
sort(v[k].begin(),v[k].end());
for(register int i=0;i<(int)v[k].size();++i)
if(v[k][i].opt==1)
tmp[k].push_back(link(v[k][i].x,v[k][i].y));
else
puts(anc(v[k][i].x)==anc(v[k][i].y)? "Y":"N");
if(l==r)
{
for(register int i=(int)tmp[k].size()-1;~i;--i)
if(tmp[k][i].first&&tmp[k][i].second)
del(tmp[k][i].first,tmp[k][i].second);
return;
}
int mid=(l+r)>>1;
query(l,mid,ls[k]);
query(mid+1,r,rs[k]);
for(register int i=(int)tmp[k].size()-1;~i;--i)
del(tmp[k][i].first,tmp[k][i].second);
}
int main()
{
n=read(),m=read();
for(register int i=1;i<=n;++i)
s[i]=1;
for(register int i=1;i<=m;++i)
{
q[i].opt=read(),q[i].x=read(),q[i].y=read();
if(q[i].opt==0)
vis[q[i].x][q[i].y]=vis[q[i].y][q[i].x]=i;
if(q[i].opt==1)
{
update(vis[q[i].x][q[i].y],i,1,m,root,q[i]);
vis[q[i].x][q[i].y]=vis[q[i].y][q[i].x]=0;
}
if(q[i].opt==2)
update(i,i,1,m,root,q[i]);
}
for(register int i=m;i;--i)
if(q[i].opt==0&&vis[q[i].x][q[i].y])
{
q[i].opt=1;
update(vis[q[i].x][q[i].y],m,1,m,root,q[i]);
q[i].opt=0;
vis[q[i].x][q[i].y]=vis[q[i].y][q[i].x]=0;
}
query(1,m,root);
return 0;
}