Luogu2542 [AHOI2005]航线规划
Solution
LCT维护边双的裸题。每次的答案是路径上边双数量-1。
维护边双可以用并查集维护,每次操作就直接作用在边双的代表元素上就行了。因为怕自己漏掉一些\(find()\),导致没有在代表元素上操作,干脆就丧心病狂地在进入任意函数的时候都\(find()\)一遍(反正复杂度也很低就是了)。当然除了缩点的函数\(combine()\)。还有,似乎涉及到父亲的都需要\(find()\)。
另外就是缩点之后代表元素要继承splay根节点的父亲,然后实儿子全都断掉。因为缩点就是把整个splay中的点缩成一个点,所以原本的实边关系都消失,只剩下虚边关系。
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define REP(i,a,b) for(int i=(a),ed=(b);i<=ed;++i)
inline int read(){
register int x=0,f=1;register char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
return f?x:-x;
}
const int N=3e4+10,M=1e5+10;
int n,m,t,ans[N],tp;
map<pair<int,int>,int> h;
struct node{int op,x,y;}e[M],q[M];
namespace LinkCutTree{
int fa[N],ch[N][2],f[N],r[N],s[N];
inline int find(int x){return f[x]=f[x]==x?f[x]:find(f[x]);}
inline void pushup(int x){s[x]=s[ch[x][0]]+s[ch[x][1]]+1;}
inline void pushr(int x){swap(ch[x][0],ch[x][1]),r[x]^=1;}
inline void pushdown(int x){if(r[x])pushr(ch[x][0]),pushr(ch[x][1]),r[x]=0;}
inline int getdir(int x){return x==ch[find(fa[x])][1];}
inline bool noroot(int x){return x==ch[find(fa[x])][0]||x==ch[find(fa[x])][1];}
inline void rotate(int x){
x=find(x);int f=find(fa[x]),p=find(fa[f]),k=getdir(x),s=ch[x][!k];
ch[x][!k]=f;ch[f][k]=s;if(noroot(f))ch[p][getdir(f)]=x;
fa[f]=x;fa[x]=p;if(s)fa[s]=f;
pushup(f);
}
inline void splay(int x){
static int stk[N];
x=find(x);int tp=0,y=x;stk[++tp]=y;
while(noroot(y))stk[++tp]=y=find(fa[y]);
while(tp)pushdown(stk[tp--]);
for(int f=find(fa[x]);noroot(x);rotate(x),f=find(fa[x]))
if(noroot(f))rotate(getdir(f)==getdir(x)?f:x);
pushup(x);
}
inline void access(int x){
x=find(x);
for(int y=0;x;y=x,x=find(fa[x]))
splay(x),ch[x][1]=y,pushup(x);
}
inline void makeroot(int x){
x=find(x);access(x);splay(x);pushr(x);
}
inline int findroot(int x){
x=find(x);access(x);splay(x);
while(ch[x][0])pushdown(x),x=ch[x][0];
splay(x);return x;
}
inline void split(int x,int y){
x=find(x),y=find(y);makeroot(x);access(y);splay(y);
}
inline void combine(int x){
pushdown(x);
if(ch[x][0])f[find(ch[x][0])]=find(x),combine(ch[x][0]);
if(ch[x][1])f[find(ch[x][1])]=find(x),combine(ch[x][1]);
}
inline void link(int x,int y){
x=find(x),y=find(y);if(x==y)return;
makeroot(x);
if(findroot(y)^x)fa[x]=y;
else{
split(x,y);combine(y);
int t=find(y);
fa[t]=find(fa[y]);ch[t][0]=ch[t][1]=0;
pushup(t);
}
}
}using namespace LinkCutTree;
int main(){
n=read(),m=read();
REP(i,1,m){
e[i]=(node){0,read(),read()};
if(e[i].x>e[i].y)swap(e[i].x,e[i].y);
h[make_pair(e[i].x,e[i].y)]=i;
}
while(1){
int op=read();if(!~op)break;
q[++t]=(node){op,read(),read()};
if(q[t].x>q[t].y)swap(q[t].x,q[t].y);
if(!op)e[h[make_pair(q[t].x,q[t].y)]].op=1;
}
REP(i,1,n)f[i]=i;
REP(i,1,m)if(!e[i].op)link(e[i].x,e[i].y);
while(t){
int x=find(q[t].x),y=find(q[t].y);
if(q[t].op){
split(x,y);
ans[++tp]=s[y]-1;
}
else link(x,y);
--t;
}
while(tp)printf("%d\n",ans[tp--]);
return 0;
}