LCT
(写在前面的话:在学LCT的那段时间,我有点丧心病狂...前几道题的代码还可以看,后面的实在是...不忍直视...)
LCT:用于解决动态树的一类数据结构
大体了解:用Splay维护每个链的信息,根据一系列操作完成相应要求。
细节掌握:
一般操作:
(1)Splay:LCT中的Splay和正常的Splay有一些区别,需要在Splay之前将从这个点到这颗Splay的根节点的路径提前PushDown。
(2)rotate:没有什么大体的区别,但是需要先处理出父亲节点与子节点之间的关系。
(3)access:不断将某个对应节点Splay到根,并且将这个节点连在虚假父节点的右子树,之后再跳到父节点继续处理一直到根节点。
(4)makeroot:基于access操作,将这个点和根相连,将这个节点Splay到对应的根节点,之后将整个Splay翻转即可,原理:因为这个Splay维护的是每个节点深度,那么更改根节点之后,所有节点的深度排名正好掉了一个顺序。
(5)link:LCT的重点操作,先makeroot一个,之后access+Splay另一个,之后将makeroot的那一个的父亲指向另一个。
(6)cut:LCT的重点操作,先makeroot一个,之后access+Splay另一个,可以发现,第一个是另一个的左儿子,之后直接将两个Splay断开即可。
(7)find:找到所在的树是哪一个,先access,之后Splay,之后再找到深度最小的(根节点),通过不停向左子树找实现。
说实话,LCT是真的好写...但是如果发现你需要调的时候,删了重写吧!
例题时间:
BZOJ2049: [Sdoi2008]Cave 洞穴勘测
分析:LCT练习题,动态维护两个节点的连通性。每次Destory就是cut,Connect就是link,Query就是find两次判断是否相等。
附上代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <iostream>
using namespace std;
#define N 10050
#define ls ch[rt][0]
#define rs ch[rt][1]
#define get(rt) (ch[f[rt]][0]!=rt)
#define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
int ch[N][2],rev[N],f[N],n,Q;
char s[15];
void PushDown(int rt)
{
if(rev[rt])
{
swap(ch[ls][0],ch[ls][1]);swap(ch[rs][0],ch[rs][1]);
rev[ls]^=1,rev[rs]^=1;rev[rt]=0;
}
}
void Update(int rt)
{
if(!isroot(rt))Update(f[rt]);
PushDown(rt);
}
void rotate(int x)
{
int y=f[x],z=f[y],k=get(x);
if(!isroot(y))ch[z][ch[z][1]==y]=x;
ch[y][k]=ch[x][!k];f[ch[y][k]]=y;
ch[x][!k]=y;f[y]=x;f[x]=z;
}
void Splay(int rt)
{
//puts("a");
Update(rt);
//puts("b");
for(int fa;!isroot(rt);rotate(rt))
{
fa=f[rt];
if(!isroot(fa))rotate(get(rt)==get(fa)?fa:rt);
}
}
void access(int rt)
{
int t=0;
while(rt){Splay(rt);rs=t;t=rt;rt=f[rt];}
}
void make_root(int rt)
{
access(rt);Splay(rt);
swap(ls,rs);rev[rt]^=1;
}
void link(int x,int rt)
{
make_root(x);f[x]=rt;
}
void cut(int x,int rt)
{
make_root(x);access(rt);Splay(rt);ls=f[x]=0;
}
int find(int rt)
{
access(rt);Splay(rt);
while(ls)PushDown(rt),rt=ls;
return rt;
}
int main()
{
scanf("%d%d",&n,&Q);
while(Q--)
{
int x,y;
scanf("%s%d%d",s,&x,&y);
if(s[0]=='C')link(x,y);
else if(s[0]=='D')cut(x,y);
else
{
x=find(x);y=find(y);
if(x==y)puts("Yes");
else puts("No");
}
}
return 0;
}
BZOJ3282: Tree
分析:LCT练习题,维护链上点权异或和
为了方便操作,建议在修改的时候先Splay到根,之后在操作,不然挺麻烦的...另外,记得在link和cut之前判断是否联通。至于点权异或和怎么维护,就是类似Splay维护区间操作的方式,大体没有什么不同。
附上代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <iostream>
using namespace std;
#define N 300050
#define ls ch[rt][0]
#define rs ch[rt][1]
#define get(rt) (ch[f[rt]][0]!=rt)
#define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
int ch[N][2],f[N],sum[N],n,m,val[N],rev[N];
void PushDown(int rt)
{
if(rev[rt])
{
swap(ch[rs][1],ch[rs][0]);swap(ch[ls][0],ch[ls][1]);
rev[rs]^=1,rev[ls]^=1,rev[rt]=0;
}
}
void PushUp(int rt){sum[rt]=val[rt]^sum[ls]^sum[rs];}
void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
void rotate(int rt)
{
int x=f[rt],y=f[x],k=get(rt);
if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
ch[x][k]=ch[rt][!k];f[ch[rt][!k]]=x;ch[rt][!k]=x;f[x]=rt;f[rt]=y;
PushUp(x);PushUp(rt);
}
void Splay(int rt)
{
int fa;
for(Update(rt);!isroot(rt);rotate(rt))
if(!isroot(f[rt]))fa=f[rt],rotate(get(fa)==get(rt)?fa:rt);
}
void access(int rt)
{
int t=0;
while(rt)Splay(rt),rs=t,PushUp(rt),t=rt,rt=f[rt];
}
void make_root(int rt)
{
access(rt);Splay(rt);
swap(ls,rs);rev[rt]^=1;
}
void link(int x,int rt){make_root(x);f[x]=rt;}
void cut(int x,int rt){make_root(x);access(rt);Splay(rt);f[x]=ls=0;}
int find(int rt)
{
access(rt);Splay(rt);
while(ls)PushDown(rt),rt=ls;
return rt;
}
void fix(int x,int v){Splay(x);sum[x]^=val[x];val[x]=v;sum[x]^=v;}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
while(m--)
{
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if(!op)
{
make_root(x);access(y);Splay(y);
printf("%d\n",sum[y]);
}else if(op==1)
{
int fx=find(x),fy=find(y);
if(fx!=fy)link(x,y);
}else if(op==2)
{
int fx=find(x),fy=find(y);
if(fx==fy)cut(x,y);
}
else fix(x,y);
}
return 0;
}
BZOJ2631: tree
分析:LCT练习题,比上一道题多了一个区间加和区间乘法。
修改的时候先makeroot一个,再access+Splay另一个,之后直接修改,记得打上lazy标记。
附上代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <iostream>
using namespace std;
#define N 100055
#define mod 51061
#define ls ch[rt][0]
#define rs ch[rt][1]
#define get(rt) (ch[f[rt]][0]!=rt)
#define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
int ch[N][2],f[N],sum[N],add[N],mul[N],rev[N],val[N],n,Q,siz[N];
void PushUp(int rt)
{
siz[rt]=siz[ls]+siz[rs]+1;
sum[rt]=(sum[ls]+sum[rs]+val[rt])%mod;
}
void PushDown(int rt)
{
if(mul[rt]!=1)
{
val[ls]=(1ll*val[ls]*mul[rt])%mod;sum[ls]=(1ll*sum[ls]*mul[rt])%mod;mul[ls]=(1ll*mul[ls]*mul[rt])%mod;add[ls]=(1ll*add[ls]*mul[rt])%mod;
val[rs]=(1ll*val[rs]*mul[rt])%mod;sum[rs]=(1ll*sum[rs]*mul[rt])%mod;mul[rs]=(1ll*mul[rs]*mul[rt])%mod;add[rs]=(1ll*add[rs]*mul[rt])%mod;
mul[rt]=1;
}
if(add[rt])
{
val[ls]=(add[rt]+val[ls])%mod;sum[ls]=((1ll*add[rt]*siz[ls])%mod+sum[ls])%mod;add[ls]=(add[rt]+add[ls])%mod;
val[rs]=(add[rt]+val[rs])%mod;sum[rs]=((1ll*add[rt]*siz[rs])%mod+sum[rs])%mod;add[rs]=(add[rt]+add[rs])%mod;
add[rt]=0;
}
if(rev[rt])
{
swap(ch[ls][0],ch[ls][1]);swap(ch[rs][0],ch[rs][1]);
rev[rs]^=1,rev[ls]^=1,rev[rt]=0;
}
}
void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
void rotate(int rt)
{
int x=f[rt],y=f[x],k=get(rt);
if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
ch[x][k]=ch[rt][!k];f[ch[x][k]]=x;
ch[rt][!k]=x;f[x]=rt;f[rt]=y;
PushUp(x);PushUp(rt);
}
void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate((get(rt)==get(f[rt]))?f[rt]:rt);}
void access(int rt){int t=0;while(rt){Splay(rt);rs=t;PushUp(rt);t=rt;rt=f[rt];}}
void make_root(int rt){access(rt);Splay(rt);swap(ls,rs);rev[rt]^=1;}
void link(int x,int rt){make_root(x);f[x]=rt;}
void cut(int x,int rt){make_root(x);access(rt);Splay(rt);f[x]=ls=0;}
void splite(int x,int rt){make_root(x);access(rt);Splay(rt);}
void Update_mul(int x,int rt,int c)
{
splite(x,rt);
mul[rt]=(1ll*mul[rt]*c)%mod;add[rt]=(1ll*add[rt]*c)%mod;
sum[rt]=(1ll*sum[rt]*c)%mod;val[rt]=(1ll*val[rt]*c)%mod;
}
void Update_add(int x,int rt,int c)
{
splite(x,rt);
sum[rt]=(sum[rt]+(1ll*siz[rt]*c)%mod)%mod;
val[rt]=(c+val[rt])%mod;add[rt]=(c+add[rt])%mod;
}
char s[10];
int main()
{
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;i++)val[i]=mul[i]=siz[i]=sum[i]=1;
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);link(x,y);
}
while(Q--)
{
int x,y,z,k;
scanf("%s%d%d",s,&x,&y);
if(s[0]=='*')
{
scanf("%d",&z);
Update_mul(x,y,z);
}else if(s[0]=='-')
{
scanf("%d%d",&z,&k);
cut(x,y);
link(z,k);
}else if(s[0]=='+')
{
scanf("%d",&z);
Update_add(x,y,z);
}else
{
splite(x,y);
printf("%d\n",sum[y]);
}
}
return 0;
}
BZOJ1180: [CROATIAN2009]OTOCI
分析:LCT练习题,维护连通性以及链上点权和
附上代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <iostream>
using namespace std;
#define N 30050
#define ls ch[rt][0]
#define rs ch[rt][1]
#define get(rt) (ch[f[rt]][0]!=rt)
#define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
int sum[N],rev[N],val[N],ch[N][2],f[N],n,Q;
char s[N];
void PushUp(int rt)
{
sum[rt]=sum[ls]+sum[rs]+val[rt];
}
void PushDown(int rt)
{
if(rev[rt])
{
swap(ch[ls][0],ch[ls][1]);swap(ch[rs][0],ch[rs][1]);
rev[ls]^=1,rev[rs]^=1,rev[rt]=0;
}
}
void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
void rotate(int rt)
{
int x=f[rt],y=f[x],k=get(rt);
if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
ch[x][k]=ch[rt][!k];f[ch[x][k]]=x;
ch[rt][!k]=x;f[x]=rt;f[rt]=y;
PushUp(x);PushUp(rt);
}
void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate((get(rt)==get(f[rt]))?f[rt]:rt);}
void access(int rt){int t=0;while(rt){Splay(rt);rs=t;PushUp(rt);t=rt;rt=f[rt];}}
void make_root(int rt){access(rt);Splay(rt);swap(ls,rs);rev[rt]^=1;}
void link(int x,int rt){make_root(x);f[x]=rt;}
int find(int rt)
{
access(rt);Splay(rt);
while(ls)PushDown(rt),rt=ls;
return rt;
}
void fix(int rt,int v){access(rt);Splay(rt);val[rt]=v;PushUp(rt);}
void splite(int x,int rt){make_root(x);access(rt);Splay(rt);}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&val[i]);
}
scanf("%d",&Q);
while(Q--)
{
int x,y;
scanf("%s%d%d",s,&x,&y);
if(s[0]=='e')
{
if(find(x)!=find(y))puts("impossible");
else
{
splite(x,y);
printf("%d\n",sum[y]);
}
}else if(s[0]=='b')
{
if(find(x)==find(y))puts("no");
else
{
puts("yes");link(x,y);
}
}else fix(x,y);
}
return 0;
}
BZOJ3669: [Noi2014]魔法森林
分析:LCT一种套路,动态维护树,贪心的将最坏的替代,之后链上最大值。
先按照a排序,做kruscal,之后动态将b最大的替换,更新答案即可。LCT只能维护点权,新建一个节点当做边吧...
附上代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <iostream>
#include <cstdlib>
using namespace std;
#define N 150050
#define ls ch[rt][0]
#define rs ch[rt][1]
#define get(rt) (ch[f[rt]][1]==rt)
#define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
int ch[N][2],f[N],rev[N],n,m,val[N],maxx[N];
struct node
{
int x,y,a,b;
}a[N];
bool cmp(const node &a,const node &b)
{
return a.a<b.a;
}
void PushUp(int rt)
{
maxx[rt]=rt;
if(val[maxx[ls]]>val[maxx[rt]])maxx[rt]=maxx[ls];
if(val[maxx[rs]]>val[maxx[rt]])maxx[rt]=maxx[rs];
}
void PushDown(int rt)
{
if(rev[rt])
{
rev[ls]^=1,rev[rs]^=1,rev[rt]=0;
swap(ch[ls][0],ch[ls][1]);swap(ch[rs][0],ch[rs][1]);
}
}
void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
void rotate(int rt)
{
int x=f[rt],y=f[x],k=get(rt);
if(!isroot(x))ch[y][get(x)]=rt;
ch[x][k]=ch[rt][!k];f[ch[x][k]]=x;
ch[rt][!k]=x;f[x]=rt;f[rt]=y;
PushUp(x);PushUp(rt);
}
void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate((get(rt)==get(f[rt]))?f[rt]:rt);}
void access(int rt){int t=0;while(rt)Splay(rt),rs=t,PushUp(rt),t=rt,rt=f[rt];}
void make_root(int rt){access(rt);Splay(rt);swap(ls,rs);rev[rt]^=1;}
void link(int x,int rt){make_root(x);Splay(rt);f[x]=rt;}
void cut(int x,int rt){make_root(x);access(rt);Splay(rt);ls=f[x]=0;}
int find(int rt){access(rt);Splay(rt);while(ls)PushDown(rt),rt=ls;return rt;}
int query(int x,int rt){make_root(x);access(rt);Splay(rt);return maxx[rt];}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].a,&a[i].b);}
sort(a+1,a+m+1,cmp);
int ans=1<<30;
for(int i=1;i<=m;i++)
{
int x=a[i].x,y=a[i].y,v1=a[i].a,v2=a[i].b;
int fx=find(x),fy=find(y);
int tot=i+n;
if(fx!=fy)
{
val[tot]=v2;maxx[tot]=tot;link(x,tot);link(tot,y);
}else
{
int tmp=query(x,y);
if(val[tmp]>v2)
{
cut(a[tmp-n].x,tmp);cut(a[tmp-n].y,tmp);val[tot]=v2;maxx[tot]=tot;
link(x,tot);link(y,tot);
}
}
if(find(1)==find(n))
{
ans=min(ans,v1+val[query(1,n)]);
}
}
printf("%d\n",(ans==1<<30)?-1:ans);
return 0;
}
BZOJ4530: [Bjoi2014]大融合
分析:LCT维护子树信息。
其实本质上还是维护一个链上信息,只是这个链上的点的信息包括了所有点的子节点的信息,也就是说,将这个树的信息维护出来,原理就是在每次access的时候,将你舍去的部分(每个点的rson)同样记录到答案之中。
附上代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <iostream>
#include <cstdlib>
using namespace std;
#define N 100505
#define ls ch[rt][0]
#define rs ch[rt][1]
#define get(rt) (ch[f[rt]][1]==rt)
#define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
int f[N],ch[N][2],siz[N],rev[N],all[N],n,Q;char s[10];
void PushDown(int rt){if(rev[rt])swap(ch[ls][0],ch[ls][1]),swap(ch[rs][0],ch[rs][1]),rev[ls]^=1,rev[rs]^=1,rev[rt]=0;}
void PushUp(int rt){all[rt]=all[ls]+all[rs]+siz[rt]+1;}
void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
void rotate(int rt)
{
int x=f[rt],y=f[x],k=get(rt);
if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
ch[x][k]=ch[rt][!k],f[ch[x][k]]=x;
ch[rt][!k]=x;f[x]=rt,f[rt]=y;
PushUp(x),PushUp(rt);
}
void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate(get(f[rt])==get(rt)?f[rt]:rt);}
void access(int rt){int t=0;while(rt)Splay(rt),siz[rt]+=all[rs]-all[t],rs=t,PushUp(rt),t=rt,rt=f[rt];}
void make_root(int rt){access(rt),Splay(rt);swap(ls,rs),rev[rt]^=1;}
void link(int x,int rt){make_root(x);make_root(rt);f[x]=rt;siz[rt]+=all[x];PushUp(rt);}
int main()
{
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;i++)all[i]=1;
while(Q--)
{
int x,y;
scanf("%s%d%d",s,&x,&y);
if(s[0]=='A')link(x,y);
else
{
make_root(x);make_root(y);
printf("%lld\n",1ll*all[x]*(all[y]-all[x]));
}
}
return 0;
}
BZOJ2594: [Wc2006]水管局长数据加强版
吐槽:好端端的题为什么要加强数据,据说不能直接link,所以写了kruscal,据说不能用map,所以重载了小于号,之后用lower_bound解决,(我*****)
分析:LCT维护动态树,Splay维护链上最大值。
删边还是离线下来往里插吧...之后类似魔法森林,然而删边不告诉编号...二分找吧...
数据范围那么大...LCT的常数感人...所以Kruscal吧...
附上代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
using namespace std;
inline char nc()
{
static char buf[100000],*p1,*p2;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd()
{
register int x=0;register char s=nc();
while(s<'0'||s>'9')s=nc();
while(s>='0'&&s<='9')x=(x<<3)+(x<<1)+s-'0',s=nc();
return x;
}
#define N 100005
#define M 1100005
#define ls ch[rt][0]
#define rs ch[rt][1]
#define get(rt) (ch[f[rt]][0]!=rt)
#define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
int ch[M][2],f[M],val[M],rev[M],mx[M],cnt,n,m,Q,fa[N],killx[M],killy[M],ans[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
struct node{int x,y,v,flag;
friend bool operator<(const node &a,const node &b){return ((a.x==b.x)&(b.y==a.y)&(a.v<b.v))|((a.x==b.x)&(a.y<b.y))|(a.x<b.x);}}e[M],ev[M];
bool cmp(const node &a,const node &b){return a.v<b.v;}
struct QAQ{int op,x,y,pos;}q[N];
void PushUp(int rt){mx[rt]=rt;if(val[mx[ls]]>val[mx[rt]])mx[rt]=mx[ls];if(val[mx[rs]]>val[mx[rt]])mx[rt]=mx[rs];}
void PushDown(int rt){if(rev[rt])swap(ch[rs][0],ch[rs][1]),swap(ch[ls][0],ch[ls][1]),rev[ls]^=1,rev[rs]^=1,rev[rt]=0;}
void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
void rotate(int rt)
{
int x=f[rt],y=f[x],k=get(rt);
if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
ch[x][k]=ch[rt][!k];f[ch[x][k]]=x;
ch[rt][!k]=x;f[x]=rt;f[rt]=y;
PushUp(x);PushUp(rt);
}
void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate((get(rt)==get(f[rt]))?f[rt]:rt);}
void access(int rt){int t=0;while(rt)Splay(rt),rs=t,PushUp(rt),t=rt,rt=f[rt];}
void make_root(int rt){access(rt);Splay(rt);rev[rt]^=1;swap(ls,rs);}
void link(int x,int rt){make_root(x);Splay(rt);f[x]=rt;}
void cut(int x,int rt){make_root(x);access(rt);Splay(rt);ls=f[x]=0;}
int query(int x,int rt){make_root(x);access(rt);Splay(rt);return mx[rt];}
int main()
{
n=rd();m=rd();Q=rd();
for(int i=1;i<=m;i++){e[i].x=rd();e[i].y=rd();e[i].v=rd();if(e[i].x>e[i].y)swap(e[i].x,e[i].y);}
sort(e+1,e+m+1);for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)ev[i]=e[i];
for(int i=1;i<=Q;i++)
{
q[i].op=rd();q[i].x=rd();q[i].y=rd();
if(q[i].op==1)continue;
if(q[i].x>q[i].y)swap(q[i].x,q[i].y);
q[i].pos=lower_bound(e+1,e+m+1,node{q[i].x,q[i].y,0,0})-e;
e[q[i].pos].flag=ev[q[i].pos].flag=1;
}
sort(ev+1,ev+1+m,cmp);int tot=n,cnt=0;
for(int i=1;i<=m;i++)
{
if(!ev[i].flag)
{
int x=ev[i].x,y=ev[i].y;
if(find(x)!=find(y))val[++tot]=ev[i].v,killx[tot]=x,killy[tot]=y,mx[tot]=tot,link(x,tot),link(tot,y),fa[find(x)]=find(y);
}
}
while(Q--)
{
int x=q[Q+1].x,y=q[Q+1].y;
if(q[Q+1].op==1)ans[++cnt]=val[query(x,y)];
else
{
int tmp=q[Q+1].pos;
if(find(x)!=find(y))val[++tot]=e[tmp].v,killx[tot]=x,killy[tot]=y,mx[tot]=tot,link(x,tot),link(tot,y),fa[find(x)]=find(y);
else
{
int t=query(x,y);
if(val[t]>e[tmp].v)val[++tot]=e[tmp].v,killx[tot]=x,killy[tot]=y,mx[tot]=tot,cut(killx[t],t),cut(t,killy[t]),link(x,tot),link(tot,y);
}
}
}
while(cnt--)printf("%d\n",ans[cnt+1]);
}
BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊
分析:转化一下变成LCT的模型,n+1个点n条边的动态树,上面都做这么多题了,大概也明白了吧...
至于查询的时候,makeroot(n+1),之后access(x)+Splay(x),可以发现,根的左子树大小即为答案,原因是答案就是n+1到x直接那条链的点数。
附上代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
using namespace std;
#define N 200005
#define ls ch[rt][0]
#define rs ch[rt][1]
#define get(rt) (ch[f[rt]][0]!=rt)
#define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
int ch[N][2],f[N],rev[N],siz[N],a[N],n,Q;
void PushUp(int rt){siz[rt]=siz[ls]+siz[rs]+1;}
void rotate(int rt)
{
int x=f[rt],y=f[x],k=get(rt);
if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
ch[x][k]=ch[rt][!k],f[ch[x][k]]=x;
ch[rt][!k]=x,f[x]=rt,f[rt]=y;PushUp(x),PushUp(rt);
}
void PushDown(int rt){if(rev[rt])swap(ch[ls][0],ch[ls][1]),swap(ch[rs][0],ch[rs][1]),rev[ls]^=1,rev[rs]^=1,rev[rt]=0;}
void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate(get(rt)==get(f[rt])?f[rt]:rt);}
void access(int rt){int t=0;while(rt)Splay(rt),rs=t,PushUp(rt),t=rt,rt=f[rt];}
void makeroot(int rt){access(rt);Splay(rt);rev[rt]^=1;swap(ls,rs);}
void link(int rt,int x){makeroot(x);Splay(rt);f[x]=rt;}
void cut(int rt,int x){makeroot(x);access(rt);Splay(rt);ls=f[x]=0;}
int query(int rt){makeroot(n+1);access(rt);Splay(rt);return siz[ls];}
int main()
{
scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]),link(i,min(i+a[i],n+1));scanf("%d",&Q);
while(Q--)
{
int op,x,y;
scanf("%d%d",&op,&x);x++;
if(op==1)printf("%d\n",query(x));
else scanf("%d",&y),cut(x,min(x+a[x],n+1)),link(x,min(y+x,n+1)),a[x]=y;
}return 0;
}
还有什么温暖会指引我们前行之类的,但是我还没有做...就先更新到这里吧...