LCT
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; }
还有什么温暖会指引我们前行之类的,但是我还没有做...就先更新到这里吧...