【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=2002
【题意】
给定n个数的序列,i可以跳到i+k[i],需要能够修改k并可以查询跳出n需要的步数。
【思路】
把i->i+k看作一条边,则问题抽象为一个森林,越靠后的点离原树的根越近。
考虑LCT维护大小siz。
修改k的操作可以看作是断边与连边的操作。注意如果直接跳出n应该与null相连以切断原来的边。
一次查询可以先Access使u到根的路径独立,然后splay将u调整至根查询siz。
【代码】
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 6 const int N = 4e5+10; 7 8 namespace LCT { 9 10 struct Node { 11 Node *ch[2],*fa; 12 int siz; 13 Node(); 14 void maintain() { 15 siz=ch[0]->siz+ch[1]->siz+1; 16 } 17 } *null=new Node,T[N]; 18 Node::Node() { fa=ch[0]=ch[1]=null; siz=1; } 19 void rot(Node* o,int d) { 20 Node *p=o->fa; 21 p->ch[d]=o->ch[d^1]; 22 o->ch[d^1]->fa=p; 23 o->ch[d^1]=p; 24 o->fa=p->fa; 25 if(p->fa->ch[0]==p) 26 p->fa->ch[0]=o; 27 else if(p->fa->ch[1]==p) 28 p->fa->ch[1]=o; 29 p->fa=o; 30 p->maintain(); 31 } 32 void splay(Node* o) { 33 Node *nf,*nff; 34 while(o->fa->ch[0]==o||o->fa->ch[1]==o) { 35 nf=o->fa,nff=nf->fa; 36 if(o==nf->ch[0]) { 37 if(nf==nff->ch[0]) rot(nf,0); 38 rot(o,0); 39 } else { 40 if(nf==nff->ch[1]) rot(nf,1); 41 rot(o,1); 42 } 43 } 44 o->maintain(); 45 } 46 void Access(Node* o) { 47 Node *son=null; 48 while(o!=null) { 49 splay(o); 50 o->ch[1]=son; 51 o->maintain(); 52 son=o; o=o->fa; 53 } 54 } 55 void Link(Node* u,Node* v) { 56 Access(u); splay(u); 57 u->ch[0]->fa=null; 58 u->ch[0]=null; 59 u->fa=v; 60 u->maintain(); 61 } 62 63 } 64 using namespace LCT; 65 66 int n,m; 67 68 int main() 69 { 70 null->fa=null->ch[0]=null->ch[1]=null; 71 null->siz=0; 72 scanf("%d",&n); 73 int op,x,y; 74 for(int i=1;i<=n;i++) { 75 scanf("%d",&x); 76 if(i+x<=n) T[i].fa=&T[i+x]; 77 } 78 scanf("%d",&m); 79 for(int i=1;i<=m;i++) { 80 scanf("%d%d",&op,&x); 81 x++; 82 if(op==1) { 83 Access(&T[x]); splay(&T[x]); 84 printf("%d ",T[x].siz); 85 } else { 86 scanf("%d",&y); 87 if(x+y<=n) Link(&T[x],&T[x+y]); 88 else Link(&T[x],null); 89 } 90 } 91 return 0; 92 }