[Luogu P3203] [HNOI2010]弹飞绵羊 (LCT维护链的长度)
题面
传送门:洛谷
Solution
这题其实是有类似模型的。
我们先考虑不修改怎么写。考虑这样做:每个点向它跳到的点连一条边,最后肯定会连成一颗以n+1为根的树(我们拿n+1代表被弹出去了)。题目所问的即是某个点到树根的链的长度。
那么,如果我们加上修改,显然,某个点连向的点会发生改变。对于一个能修改边的树,我们可以很自然的想到用LCT维护之。
至于怎么求某条链的长度呢?这也是LCT的基础操作之一,我们只需要先MakeRoot(n+1),然后再Acess(x),splay(x)就可以把这条链拉出来了,我们维护splay的size就好。
如果您看不懂上面这句话,请戳我来学习LCT与原树的对应关系
Code
//Luogu P3203 [HNOI2010]弹飞绵羊 //Jan,9th,2018 //LCT模板题II #include<iostream> #include<cstdio> using namespace std; long long read() { long long x=0,f=1; char c=getchar(); while(!isdigit(c)){if(c=='-') f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } const int N=200000+100; int n,m; struct LCT { int son[N][2],fa[N],lazy[N],mstack[N],top,size[N]; inline bool isRoot(int x) { return x!=son[fa[x]][0] && x!=son[fa[x]][1]; } inline void update(int x) { size[x]=size[son[x][0]]+size[son[x][1]]+1; } inline void mirror(int x) { lazy[x]=!lazy[x],swap(son[x][0],son[x][1]); } inline void pushDown(int x) { if(lazy[x]==0) return; lazy[x]=0; mirror(son[x][0]),mirror(son[x][1]); } inline void rotate(int x,int type) { int y=fa[x],z=fa[y]; if(isRoot(y)==false) son[z][y==son[z][1]]=x; fa[x]=z; son[y][!type]=son[x][type],fa[son[x][type]]=y; son[x][type]=y,fa[y]=x; update(y),update(x); } void splay(int x) { mstack[top=1]=x; for(int i=x;isRoot(i)==false;i=fa[i]) mstack[++top]=fa[i]; for(int i=top;i>=1;i--) pushDown(mstack[i]); while(isRoot(x)==false) { if(x==son[fa[x]][fa[x]==son[fa[fa[x]]][1]] and isRoot(fa[x])==false) rotate(fa[x],x==son[fa[x]][0]), rotate(x,x==son[fa[x]][0]); else rotate(x,x==son[fa[x]][0]); } } void Access(int x) { for(int t=0;x!=0;t=x,x=fa[x]) splay(x),son[x][1]=t,fa[t]=x,update(x); } inline void MakeRoot(int x) { Access(x),splay(x); mirror(x); } inline void Link(int x,int y)//x翻为根连向y { MakeRoot(x); fa[x]=y; } inline void split(int x,int y)//y为根 { MakeRoot(x); Access(y),splay(y); } inline void Cut(int x,int y) { split(x,y); son[y][0]=fa[x]=0; update(y); } int Query(int x) { MakeRoot(n+1); Access(x),splay(x); return size[x]-1; } }lct; int q[N]; int main() { n=read(); for(int i=1;i<=n;i++) q[i]=read(); for(int i=n;i>=1;i--) if(i+q[i]>n) lct.Link(i,n+1); else lct.Link(i,i+q[i]); m=read(); for(int i=1;i<=m;i++) { int op=read(); if(op==1) { int x=read()+1; printf("%d\n",lct.Query(x)); } else { int x=read()+1,K=read(); if(x+q[x]>n) lct.Cut(x,n+1); else lct.Cut(x,x+q[x]); q[x]=K; if(x+q[x]<=n) lct.Link(x,x+q[x]); else lct.Link(x,n+1); } } return 0; }
自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。