直线上有一排n个弹力装置,每个弹力装置会将绵羊弹到下ki个弹力装置处;
如果没有了则绵羊被弹飞。。
问每个绵羊被弹了几次弹飞;
可能会修改弹力装置的k值;
n<=200000,m<=100000;
题解:
裸的LCT吧;
所以下面的启发式合并Splay是啥鬼;
有人说这题边有向,和无向边不一样;
然而有个卵区别,把终点作为根不就有向了吗!
反正切了上一题这一题也不难吧;
维护个size之后,把终点作为根再access(x);
这时的Splay就是x弹飞的路线啦,Splay(x)之后x的左子树大小就是答案咯;
其实也是整棵Splay的大小-1,因为x的重儿子在access时砍掉了嘛;
LCT是啥啥的还是戳上一篇。。
代码:
#include<stdio.h> #include<string.h> #include<algorithm> #define N 210000 #define which(x) (ch[fa[x]][1]==x) using namespace std; int fa[N],ch[N][2],size[N],to[N],root; bool rt[N],cov[N]; void Pushup(int x) { size[x]=size[ch[x][0]]+size[ch[x][1]]+1; } void reverse(int x) { swap(ch[x][0],ch[x][1]); cov[x]^=1; } void Pushdown(int x) { if(cov[x]) { reverse(ch[x][0]); reverse(ch[x][1]); cov[x]=0; } } void down(int x) { if(!rt[x]) down(fa[x]); Pushdown(x); } void Rotate(int x) { int f=fa[x]; bool k=which(x); if(rt[f]) rt[f]^=rt[x]^=1; else ch[fa[f]][which(f)]=x; ch[f][k]=ch[x][!k]; ch[x][!k]=f; fa[ch[f][k]]=f; fa[x]=fa[f]; fa[f]=x; size[x]=size[f]; Pushup(f); } void Splay(int x) { down(x); while(!rt[x]) { int f=fa[x]; if(rt[f]) { Rotate(x); return ; } if(which(x)^which(f)) Rotate(x); else Rotate(f); Rotate(x); } } void access(int x) { int y=0; while(x) { Splay(x); rt[ch[x][1]]=1; rt[y]=0; ch[x][1]=y; Pushup(x); y=x,x=fa[x]; } } void Mtr(int x) { access(x); Splay(x); reverse(x); } void Link(int x,int y) { Mtr(x); fa[x]=y; } void Cut(int x,int y) { Mtr(x); access(y); Splay(x); ch[x][1]=0,fa[y]=0; rt[y]=1; Pushup(x); } int Query(int x) { Mtr(root); access(x); Splay(x); return size[ch[x][0]]; } int main() { int n,m,i,j,k,x,y,op; scanf("%d",&n); root=n+1; for(i=1;i<=n+1;i++) rt[i]=1,size[i]=1; for(i=1;i<=n;i++) { scanf("%d",&k); to[i]=min(i+k,root); Link(i,to[i]); } scanf("%d",&m); for(i=1;i<=m;i++) { scanf("%d",&op); if(op==1) { scanf("%d",&x),x++; printf("%d\n",Query(x)); } else { scanf("%d%d",&x,&k),x++; Cut(x,to[x]); to[x]=min(x+k,root); Link(x,to[x]); } } return 0; }
原文地址:http://blog.csdn.net/ww140142/article/details/47683051