[BZOJ 2002] [HNOI2010]弹飞绵羊(Link Cut Tree)
题面
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
分析
首先把所有点编号+1,建立虚拟节点n+1表示被弹飞。如果i+ki>n就连边(i,n+1)否则连边(i,i+ki)。
修改就是先删边再加边
查询其实就是查询x到n+1的路径长度.split(x,n+1)后的子树大小-1即为答案
代码
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 200000 using namespace std; struct link_cut_tree{ #define lson(x) (tree[x].ch[0]) #define rson(x) (tree[x].ch[1]) #define fa(x) (tree[x].fa) struct node{ int ch[2]; int fa; int sz; int revm; }tree[maxn+5]; inline bool is_root(int x){ return !(lson(fa(x))==x||rson(fa(x))==x); } inline int check(int x){ return rson(fa(x))==x; } void push_up(int x){ tree[x].sz=tree[lson(x)].sz+tree[rson(x)].sz+1; } void reverse(int x){ swap(lson(x),rson(x)); tree[x].revm^=1; } void push_down(int x){ if(tree[x].revm){ reverse(lson(x)); reverse(rson(x)); tree[x].revm=0; } } void push_down_all(int x){ if(!is_root(x)) push_down_all(fa(x)); push_down(x); } void rotate(int x){ int y=fa(x),z=fa(y),k=check(x),w=tree[x].ch[k^1]; tree[y].ch[k]=w; tree[w].fa=y; if(!is_root(y)) tree[z].ch[check(y)]=x; tree[x].fa=z; tree[x].ch[k^1]=y; tree[y].fa=x; push_up(y); push_up(x); } void splay(int x){ push_down_all(x); while(!is_root(x)){ int y=fa(x); if(!is_root(y)){ if(check(x)==check(y)) rotate(y); else rotate(x); } rotate(x); } push_up(x); } void access(int x){ for(int y=0;x;y=x,x=fa(x)){ splay(x); rson(x)=y; push_up(x); } } void make_root(int x){ access(x); splay(x); reverse(x); } void split(int x,int y){ make_root(x); access(y); splay(y); } void link(int x,int y){ make_root(x); fa(x)=y; } void cut(int x,int y){ split(x,y); lson(y)=fa(x)=0; push_up(y); } int query(int x,int y){//查询x到y的路径长度,即split出来的子树大小-1 split(x,y); return tree[y].sz-1; } }T; int n,m; int a[maxn+5]; //虚拟节点n+1表示被弹飞 inline int get_nex(int x,int k){//找到被弹到的节点 if(x+k<=n) return x+k; else return n+1; } int main(){ int cmd,x,k; scanf("%d",&n); for(int i=1;i<=n+1;i++) T.tree[i].sz=1; // T.link(4,5); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); T.link(i,get_nex(i,a[i])); } scanf("%d",&m); for(int i=1;i<=m;i++){ scanf("%d",&cmd); if(cmd==1){ scanf("%d",&x); x++; printf("%d\n",T.query(x,n+1));//查询x到n+1路径长度 }else{ scanf("%d %d",&x,&k); x++; T.cut(x,get_nex(x,a[x])); a[x]=k; T.link(x,get_nex(x,a[x])); } } }