题目:BZOJ2002、洛谷P3203、codevs2333。
题目大意:有n个装置,编号0~n-1。每个装置有一个弹跳系数,第i个装置可以把羊弹到第$i+k_i$个装置上,然后继续弹。如果这个位置没有装置,则停止弹。
现在有m个操作,可以问你一只绵羊在某点开始弹,弹多少次才能结束,也可以修改一个装置的弹跳系数。
解题思路:分块。
一般都是每块分成$\sqrt{n}$个(最后一个可能不同),分成$\sqrt{n}$或$\sqrt{n}+1$块。
设jp[i]表示从这个点跳到另一个块的某个点要多少下,nxt[i]表示从i开始跳,跳到另一个块后,跳到的节点的编号。则可以发现,更新一个弹跳系数,只会变化该块内的答案(一个点可能跳到相同块内一个点)。
然后记录下每个块的起点,更新答案时一个一个更新即可(注意倒着更新)。
输出弹跳次数则更加简单,每次累计jp[i],然后令i=nxt[i]即可。
更新答案最多涉及一整个块,输出弹跳次数最多每个块有一个节点被访问,所以一次处理的时间复杂度为$O(\sqrt{n})$。
则总时间复杂度为$O(m\sqrt{n})$。
当然您也可以用LCT秒了这道题。
C++ Code:
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cmath>
#define N 200020
int n,size,jp[N],nxt[N],k[N],l[505],inbk[N],blocks;
inline int readint(){
char c=getchar();
for(;!isdigit(c);c=getchar());
int d=0;
for(;isdigit(c);c=getchar())
d=(d<<3)+(d<<1)+(c^'0');
return d;
}
int ask(int s){
int ans=0;
while(s!=-1){
ans+=jp[s];
s=nxt[s];
}
return ans;
}
int main(){
n=readint();
size=(int)(sqrt(n)+0.00000001);
blocks=0;
l[0]=0;
for(int i=0;i<n;++i){
k[i]=readint();
inbk[i]=blocks;
if(i%size==size-1)l[++blocks]=i+1;
}
if(n%size)++blocks;
memset(nxt,-1,sizeof nxt);
for(int i=n-1;i>=0;--i){
if(i+k[i]>=n)jp[i]=1;else
if(inbk[i]==inbk[i+k[i]])
jp[i]=jp[i+k[i]]+1,nxt[i]=nxt[i+k[i]];else
jp[i]=1,nxt[i]=i+k[i];
}
for(int m=readint();m--;){
int f=readint();
if(f==1)printf("%d\n",ask(readint()));else{
int x=readint();
k[x]=readint();
for(int i=x;i>=l[inbk[x]];--i)
if(inbk[i]==inbk[i+k[i]])jp[i]=jp[i+k[i]]+1,nxt[i]=nxt[i+k[i]];else
jp[i]=1,nxt[i]=i+k[i];
}
}
return 0;
}