题目链接:luogu2542
先考虑没有修改操作应该如何完成询问:使用(Tarjan)对点双进行缩点,答案就是缩完点后两点之间的距离
再考虑有修改的情况,注意到修改只有删除一种操作,于是可以考虑倒序加边
使用(LCT)维护这个加边的过程,考虑每一次加边对图中点双的影响:如果连成了一个环,那么就将这个环缩成一个点
采用线段树打标记的思想,我们再开一个并查集,来维护缩点之后每个点的编号。具体的,对于每次加边,我们先找到连边的两点在缩完点之后的编号,若不在一个连通块的话则直接连边,否则就要考虑在并查集上打上缩点的tag。注意到我们(makeroot(x),access(y))之后这棵splay维护的就是(x->y)的链了,于是直接暴力的将(x)的右儿子清空,并在并查集上将它们的父亲置为(x)
但这只是一个打标记的过程,我们并没有对子树信息做实质性的修改
由于查询子树信息时都需要(access),于是可以在这个上面动手,将原来的(x=fa[x])变成(fa[x])缩成的那个点即可
具体的更多细节看代码
#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define fir first
#define sec second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 998244353
#define eps 1e-8
struct node{
int u,v;
}edge[200200];
struct qnode{
int op,x,y;
}q[200200];
int n,m,siz[30050],fa[30030],ch[30030][2],pa[30030],tag[30030],sta[30030],tot=0,tot1=0,ans[40030];
map<pair<int,int>,int> used;
int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
bool isroot(int x) {return ((ch[fa[x]][0]!=x) && (ch[fa[x]][1]!=x));}
void pushup(int x) {siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;}
void puttag(int x) {swap(ch[x][0],ch[x][1]);tag[x]^=1;}
int find(int x) {if (pa[x]==x) return x;pa[x]=find(pa[x]);return pa[x];}
void pushdown(int x)
{
if (tag[x])
{
if (ch[x][0]) puttag(ch[x][0]);
if (ch[x][1]) puttag(ch[x][1]);
tag[x]=0;
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=(ch[y][1]==x),w=ch[x][k^1];
if (!isroot(y)) ch[z][ch[z][1]==y]=x;
ch[x][k^1]=y;ch[y][k]=w;
if (w) fa[w]=y;fa[y]=x;fa[x]=z;
pushup(y);pushup(x);
}
void splay(int x)
{
int now=x,tp=0;sta[++tp]=now;
while (!isroot(now)) {now=fa[now];sta[++tp]=now;}
while (tp) {pushdown(sta[tp]);tp--;}
while (!isroot(x))
{
int y=fa[x],z=fa[y];
if (!isroot(y))
{
if ((ch[y][0]==x)^(ch[z][0]==y)) rotate(x);else rotate(y);
}
rotate(x);
}
pushup(x);
}
void access(int x)
{
int y=0;
while (x)
{
splay(x);ch[x][1]=y;pushup(x);
y=x;fa[y]=find(fa[y]);x=find(fa[x]);
}
}
int findroot(int x)
{
access(x);splay(x);
while (ch[x][0])
{
pushdown(x);x=ch[x][0];
}
splay(x);return x;
}
void makeroot(int x) {access(x);splay(x);puttag(x);}
void split(int x,int y) {makeroot(x);access(y);splay(y);}
void del(int x,int fx) {if (x) {pa[x]=fx;del(ch[x][0],fx);del(ch[x][1],fx);}}
void link(int x,int y)
{
if (x==y) return;
makeroot(x);
if (findroot(y)!=x)
{
fa[x]=y;pushup(y);
}
else
{
del(ch[x][1],x);
ch[x][1]=0;pushup(x);
}
}
int main()
{
n=read();m=read();
rep(i,1,m)
{
edge[i].u=read();edge[i].v=read();
}
while (1)
{
int op=read();if (op==-1) break;
q[++tot].op=op;
q[tot].x=read();q[tot].y=read();
if (!op) used[mp(q[tot].x,q[tot].y)]=used[mp(q[tot].x,q[tot].y)]=1;
}
rep(i,1,n) {siz[i]=1;pa[i]=i;}
rep(i,1,m)
{
if (used.count(mp(edge[i].u,edge[i].v))==0)
{
int fx=find(edge[i].u),fy=find(edge[i].v);
link(fx,fy);
}
}
per(i,tot,1)
{
int fx=find(q[i].x),fy=find(q[i].y);
if (q[i].op)
{
split(fx,fy);ans[++tot1]=siz[fy]-1;
}
else link(fx,fy);
//rep(j,1,n) cout << find(j) << " ";cout << endl;
}
per(i,tot1,1) printf("%d
",ans[i]);
return 0;
}