先简单讲一下LCT做法,每个点以它的到达点为父节点(没有到达点就连向根),结果就是深度。然后修改就是改变父亲,一个cut再一个link就好了。
然后是分块。每个点维护它到达它所在块外面的点,及步数,预处理O(N)。查询时O(N/m)。修改时只需要修改x所在块在x前面的那些点即可,时间O(m)。因此总时间复杂度O(M*(N/m+m)+N)=O(MN^0.5+N)(取m=N^0.5)
AC代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#define N 200005
using namespace std;
int n,m,a[N],nxt[N],len[N],blg[N],l[5005],r[5005];
int read(){
int x=0; char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
return x;
}
int main(){
n=read(); m=(int)sqrt(n); int i,j;
for (i=1; i<=n; i++) a[i]=read();
for (i=1; i<=m; i++){
l[i]=r[i-1]+1; r[i]=r[i-1]+m;
}
r[m]=n;
for (i=1; i<=m; i++)
for (j=l[i]; j<=r[i]; j++) blg[j]=i;
for (i=n; i; i--){
int x=i+a[i];
if (x>n){ nxt[i]=0; len[i]=1; }
else if (x>r[blg[i]]){ nxt[i]=x; len[i]=1; }
else{ nxt[i]=nxt[x]; len[i]=len[x]+1; }
}
int cas=read();
while (cas--){
int k=read(),x=read()+1;
if (k==1){
int ans=0; for (i=x; i; i=nxt[i]) ans+=len[i];
printf("%d\n",ans);
} else{
int y=read(),t=blg[x]; a[x]=y;
for (i=x; i>=l[t]; i--){
int tmp=i+a[i];
if (tmp>n){ nxt[i]=0; len[i]=1; }
else if (tmp>r[t]){ nxt[i]=tmp; len[i]=1; }
else{ nxt[i]=nxt[tmp]; len[i]=len[tmp]+1; }
}
}
}
return 0;
}
by lych
2016.2.23