[BZOJ 2002][Luogu 3203][HNOI2010]Bounce 弹飞绵羊
<题意概括>
给定一个数列$\left\{k_{N}\right\}$,数列长度为n,有k次操作
要求支持两种操作
1.求最少对数x进行几次变换才能满足条件$x>n$(一次操作定义为$x=x+k_{x}$)
2.将$k_{x}$修改为a
<做法>
通过建模我们可以将题目转化为如下的问题
建一个图,将图中每个顶点i和顶点$i+k_{i}$连边
那么操作一就转化为了求顶点i到任一编号大于n的顶点的最短路长度
但是这样可能有很多编号大于n的顶点,不方便统计
于是我们将编号大于n的顶点缩为一个编号为$n+1$的点
这样操作一就相当于求顶点i到顶点$n+1$的最短路长度
操作二则是断开边(i,$i+k_{i}$),将$k_{i}$修改为a并连一条新边(i,$i+k_{i}$)
由操作二的动态断边连边可以想到使用Link-Cut Tree去维护这个图
则操作一就是MakeRoot(x),Access(n+1),Splay(n+1),此时$Answer=Size_{n+1}-1$
因为在MakeRoot(x)并Access(n+1),Splay(n+1)后,链$x\leftrightarrow n+1$全在以root为n+1的Auxiliary Tree上
故Answer就是该Auxiliary Tree的root的Size-1(除去点n+1)
操作二就是Cut(x,x+K[x]>N?N+1:x+K[x]);K[x]=y;Link(x,x+K[x]>N?N+1:x+K[x]);
<Code>
#include<cstdio> #include<algorithm> using namespace std; #define Fast register inline char Getchar(){ static char BUF[16384],*S=BUF,*T=BUF; return(S==T)&&(T=(S=BUF)+fread(BUF,1,16384,stdin),S==T)?EOF:*S++; } inline int Getint(){ Fast int s=0;Fast char c=Getchar(); while(c<48||c>57)c=Getchar(); while(c>47&&c<58)s=s*10+c-48,c=Getchar(); return s; } struct Node{ int lson,rson,p,size; bool rev; Node():lson(0),rson(0),p(0),size(0),rev(false){} Node(int key):lson(0),rson(0),p(0),size(0),rev(false){} }node[200002]; #define ls(o) node[o].lson #define rs(o) node[o].rson #define Fa(o) node[o].p #define Size(o) node[o].size #define Rev(o) node[o].rev inline void update(const int&o){if(o)Size(o)=Size(ls(o))+Size(rs(o))+1;} inline void push_down(const int&o){ if(!Rev(o))return; swap(ls(o),rs(o)); Rev(ls(o))^=1; Rev(rs(o))^=1; Rev(o)=false; } inline bool IsRoot(const int&o){return ls(Fa(o))!=o&&rs(Fa(o))!=o;} inline void rotate(const int&o){ Fast int p=Fa(o),gp=Fa(p); if(!IsRoot(p)){ if(ls(gp)==p)ls(gp)=o; else rs(gp)=o; } Fa(o)=gp; Fa(p)=o; if(ls(p)==o)Fa(rs(o))=p,ls(p)=rs(o),rs(o)=p; else Fa(ls(o))=p,rs(p)=ls(o),ls(o)=p; update(p); update(o); } inline void Splay(const int&o){ int Stack[200001]; Fast int Top=0; Stack[++Top]=o; for(Fast int i=o;!IsRoot(i);i=Fa(i))Stack[++Top]=Fa(i); while(Top)push_down(Stack[Top--]); while(!IsRoot(o)){ if(!IsRoot(Fa(o))){ if(ls(Fa(o))==o^ls(Fa(Fa(o)))==Fa(o))rotate(o); else rotate(Fa(o)); } rotate(o); } } inline void Access(int o){ for(Fast int k=0;o;k=o,o=Fa(o)){ Splay(o); rs(o)=k; update(o); } } inline void MakeRoot(const int&o){ Access(o); Splay(o); Rev(o)^=1; } inline int Find(int o){ Access(o); Splay(o); while(ls(o))o=ls(o); return o; } inline void Link(const int&x,const int&y){ MakeRoot(x); Fa(x)=y; } inline void Cut(const int&x,const int&y){ MakeRoot(x); Access(y); Splay(y); if(ls(y)==x)ls(y)=Fa(x)=0; } int K[200001]; int main(){ Fast int N=Getint(),M,x,y,opt; for(Fast int i=1;i<=N;++i)K[i]=Getint(),Link(i,i+K[i]>N?N+1:i+K[i]); M=Getint()+1; while(--M){ opt=Getint(),x=Getint()+1; if(opt&1){ MakeRoot(x); Access(N+1); Splay(N+1); printf("%d\n",Size(N+1)-1); } else{ y=Getint(); Cut(x,x+K[x]>N?N+1:x+K[x]); K[x]=y; Link(x,x+K[x]>N?N+1:x+K[x]); } } return 0; }