题目概述
有n个弹力装置,每个弹力装置i弹力系数为ki,会弹到i+ki,如果不存在i+ki则被弹飞,给出m个操作/询问,操作:将某个装置的弹力系数改变,询问:询问从某个弹力装置开始弹几次会被弹飞。
解题报告
加入一个n+1点表示弹飞,那么每个弹力装置就只会有一个目的地,但一个目的地可能会有很多儿子,所以是一棵树,用LCT进行维护。每次询问的话只需要将x->n+1的路径拿出来,求路径长度(节点个数-1)即可。
示例程序
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=200000;
int n,te,K[maxn+5];
struct LCT
{
int father[maxn+5],son[maxn+5][2],si[maxn+5];
bool flip[maxn+5];
void Pushup(int p) {si[p]=si[son[p][0]]+1+si[son[p][1]];}
void Add_flip(int p) {swap(son[p][0],son[p][1]);flip[p]^=1;}
void Pushdown(int p)
{
if (!flip[p]) return;
if (son[p][0]) Add_flip(son[p][0]);
if (son[p][1]) Add_flip(son[p][1]);
flip[p]=false;
}
bool is_ro(int p) {return p!=son[father[p]][0]&&p!=son[father[p]][1];}
void Rotate(int p)
{
int fa=father[p],d=p==son[fa][0];
if (!is_ro(fa)) son[father[fa]][fa==son[father[fa]][1]]=p;
son[fa][d^1]=son[p][d];father[son[p][d]]=fa;son[p][d]=fa;
Pushup(fa);Pushup(p);father[p]=father[fa];father[fa]=p;
}
int top,stk[maxn+5];
void Splay(int p) //用非递归Splay比较方便,省去了找p的过程
{
stk[++top]=p;
for (int i=p;!is_ro(i);i=father[i]) stk[++top]=father[i];
while (top) Pushdown(stk[top--]); //因为非递归,所以要先将上面的点传标记
while (!is_ro(p))
{
int fa=father[p];
if (!is_ro(fa))
{
int d1=fa==son[father[fa]][1],d2=p==son[fa][1];
if (d1==d2) Rotate(fa); else Rotate(p);
}
Rotate(p);
}
}
void Access(int p)
{
int lst=0;
while (p)
{
Splay(p);son[p][1]=lst;Pushup(p);
lst=p;p=father[p];
}
}
void make_ro(int p) {Access(p);Splay(p);Add_flip(p);}
void Link(int x,int y) {Access(y);make_ro(y);father[y]=x;}
void Cut(int x,int y)
{
make_ro(y);Access(x);Splay(x);
father[y]=son[x][0]=0;Pushup(x);
}
};
LCT tr;
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x)
{
int tot=0,f=1;char ch=getchar(),lst='+';
while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}
if (lst=='-') f=-f;
while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
x=tot*f;
return Eoln(ch);
}
void First_init() {for (int i=1;i<=n+1;i++) tr.si[i]=1;}
int main()
{
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
readi(n);First_init();
for (int i=1;i<=n;i++) readi(K[i]),tr.Link(min(i+K[i],n+1),i); //初始边
readi(te);
while (te--)
{
int td,x,y;readi(td);readi(x);x++;
if (td==1)
{
tr.make_ro(n+1);tr.Access(x);tr.Splay(x);
printf("%d\n",tr.si[x]-1);
} else
readi(y),tr.Cut(min(x+K[x],n+1),x),tr.Link(min(x+(K[x]=y),n+1),x);
//和原来的目的地断开,连接到新的目的地上
}
return 0;
}