Description
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
Input
第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1,接下来一行有n个正整数,依次为那n个装置的初始弹力系数。第三行有一个正整数m,接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000
Output
对于每个i=1的情况,你都要输出一个需要的步数,占一行。
用块状树维护由跳跃一次后位置到跳跃前位置连边构成的有根树。
每操作O(n0.5)次重构整棵树
均摊时间复杂度O(n1.5)
#include<cstdio> #include<cmath> int n,m,rt; int cs,cb,tt=0,cb2; int fa[200005],fw[200005],v[200005],dep[200005],top[200005]; int enx[800005],epv[800005],ew[800005],ep; inline int min(int a,int b){ return a<b?a:b; } void addedge(int a,int b){ fa[b]=a; ew[ep]=b; enx[epv[ep]=epv[a]]=ep; enx[ep]=a; fw[b]=epv[a]=ep++; } void deledge(int a){ int x=fw[a],pv=epv[x]; epv[enx[pv]=enx[x]]=pv; } int getdep(int x){ int ans=-1; while(x){ ans+=dep[x]; x=fa[top[x]]; } return ans; } void build(int w){ ++cs; if(cs<=cb)top[w]=top[fa[w]],dep[w]=dep[fa[w]]+1; else cs=0,top[w]=w,dep[w]=1; for(int i=enx[w],u=ew[i];u=ew[i];i=enx[i])build(u); } int tp,tpn,dp; void dfs(int w){ if(top[w]!=tp)return; top[w]=tpn; dep[w]=dp; ++dp; for(int i=enx[w],u=ew[i];u=ew[i];i=enx[i])dfs(u); --dp; } int main(){ scanf("%d",&n); cb=sqrt(n); cb2=cb*3; rt=n+1; for(int i=1;i<=n;i++)scanf("%d",v+i); for(int i=1;i<=rt;i++)enx[i]=epv[i]=i;ep=rt+1; for(int i=1;i<=n;i++)addedge(min(i+v[i],rt),i); scanf("%d",&m); cs=cb,build(rt); while(m--){ int op,a,b; scanf("%d",&op); if(op==2){ scanf("%d%d",&a,&b);++a; v[a]=b; deledge(a); addedge(min(a+v[a],rt),a); tp=top[a],tpn=a,dp=1,dfs(a); if(++tt>cb2)cs=cb,build(rt),tt=0; }else{ int vans; scanf("%d",&a);++a; printf("%d\n",vans=getdep(a)); } } return 0; }