BZOJ2002 [Hnoi2010]Bounce 弹飞绵羊
AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=2002
[分析]
看完LCT的理论分析,找入门题熟悉一下代码的时候...就看了这题
思路感觉比较明确:因为跳跃就是连边的感觉,而且每个点的出度都等于1,这不就是一棵树吗(当然所有能弹飞的都连到根上)
当然这种树的形态会改变,正好符合动态树的处理范畴.询问的是需要几次弹出去,也就是深度比它浅的有多少,这个也可以用splay做.
但是我抄的是kuangbin的模板.感觉很诡异...因为给你的树的father和平衡树里用的father是一样的....惊恐!旋转splay的时候是可以改变father的值的,也就是每次翻转都会让这棵树的形态变化!
然而还是可以A,不得不服....
这便需要反思了...因为每次询问都会打通任督二脉(雾)是Access这个节点[这里利用了根节点的father为0来打通],那么不管你之前怎么改变了树的形态,这个节点最后还是找到了自己到根节点的这条链[虽然现在可能不再是一条链,但会在一颗二叉平衡树里],旋转之后得到的答案也是正确的.而删除某条边的时候,也是先将这个边的发出点Access,也就处理了它后面节点的后顾之忧[断开了],再Splay就让它在树根的位置而且注定没有右子树[之前被断开了],让左子树成为新的平衡树树根就好[前面的节点也没有变化],而添加的时候也只是连虚边,这样操作就是没有问题的.在操作中树的形态不是很重要,但是整个的到根的一条链能通过Access找到,这个很重要.
代码[%kuangbin]:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int in(){ int x=0,f=1;char ch=getchar(); while((ch>'9' || ch<'0') && ch!='-') ch=getchar(); if(ch=='-') ch=getchar(),f=-1; while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x*f; } const int maxn=200010; struct Node{ int l,r,f,sz; bool rt; }s[maxn]; int n,m; void update(int x){ s[x].sz=s[s[x].l].sz+s[s[x].r].sz+1; } void zig(int x){ int y=s[x].f; s[x].f=s[y].f; if(s[y].rt) s[x].rt=true,s[y].rt=false; else{ if(s[s[y].f].l==y) s[s[y].f].l=x; else s[s[y].f].r=x; } s[y].l=s[x].r; if(s[x].r) s[s[x].r].f=y; s[y].f=x,s[x].r=y; update(y),update(x); } void zag(int x){ int y=s[x].f; s[x].f=s[y].f; if(s[y].rt) s[x].rt=true,s[y].rt=false; else{ if(s[s[y].f].l==y) s[s[y].f].l=x; else s[s[y].f].r=x; } s[y].r=s[x].l; if(s[x].l) s[s[x].l].f=y; s[y].f=x,s[x].l=y; update(y),update(x); } void Splay(int x){ int y; while(!s[x].rt){ y=s[x].f; if(s[y].rt){ if(s[y].l==x) zig(x); else zag(x);} else{ int z=s[y].f; if(y==s[z].l){ if(x==s[y].l) zig(y),zig(x); else zag(x),zig(x);} else{ if(x==s[y].r) zag(y),zag(x); else zig(x),zag(x);} } } } /* Access(x)表示的是将x与根节点相连的过程 s[i].r[平衡树中的右节点] 在实际树中的意义是接在i后的点 last表示的是现在x所在的链需要与之前x所在的链相连,也就需要将原本连接的部分舍去 last初始为0,可以让x一开始便与后面的链断开 所以操作就是一步步地往上爬,每次将上次的位置和这次的连上,直到根的过程 */ void Access(int x){ int last=0; for(;x;x=s[last=x].f){ Splay(x); s[s[x].r].rt=true; s[x].r=last; s[last].rt=false; update(x); } } int main(){ #ifndef ONLINE_JUDGE freopen("2002.in","r",stdin); freopen("2002.out","w",stdout); #endif int t,ord,x,d; n=in(); for(int i=1;i<=n;i++){ t=in()+i; s[i].f=min(t,n+1); s[i].rt=true; s[i].sz=1; } s[0].rt=s[n+1].rt=true,s[n+1].sz=1; m=in(); while(m--){ ord=in(); if(ord==1){ x=in()+1; Access(x),Splay(x); printf("%d\n",s[s[x].l].sz); } else{ x=in()+1;d=in(); Access(x),Splay(x); s[s[x].l].f=s[x].f; s[s[x].l].rt=true; s[x].l=0,s[x].f=0; update(x); s[x].f=min(n+1,x+d); } } return 0; }
后来与同学讨论了一下...对这种记录方法有所改观.
father其实有两种含义,当rt=true的时候,这个点事实上记录的是这条链的path_father这也就证明了之前Access的复杂度(每次splay之后都是直接跳链)
当rt=false的时候,father就是单纯的splay中的用法了,用来进行zig,zag,splay操作.
好神啊!