Problem link
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的情况,你都要输出一个需要的步数,占一行。
Sample Input
4
1 2 1 1
3
1 1
2 1 1
1 1
Sample Output
2
3
Solution
这题是一道LCT的例题,然而我怎么会LCT呢,于是就有分块大暴力搞定了。
首先高呼:分块大法好!分块大法好!分块大法好!
首先,分块,对于每一个点维护两个东西:跳出这个块需要多少部,以及跳出之后到了哪个点。
预处理O(N),要注意如果你打了
O(nn√)
的时间,在BZOJ的慢速评测下就会T
对于询问,最多只会跳
n√
次,时间复杂度为
O(n√)
对于修改,暴力修改这个块内的点,一个块只有
n√
个点,时间复杂度
O(n√)
总时间复杂度
O(mn√)
(这句是一年以后加的)但是现在我会LCT了,点击这里进入
Code
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define big 448
int n,a[201000],b[201000],c[201000],ac,be[201000];
int main()
{
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]),be[i]=(i-1)/big+1;
b[n]=n+1;c[n]=0;
for(int i=n;i;i--)
if(be[i]==be[i+a[i]]) c[i]=c[i+a[i]]+1,b[i]=b[i+a[i]];
else c[i]=1,b[i]=i+a[i];
for(scanf("%d",&ac);ac;ac--)
{
int jy;scanf("%d",&jy);
if(jy==1)
{
int x,ans=0;scanf("%d",&x);x++;
for(;x<=n;x=b[x]) ans+=c[x];
printf("%d\n",ans);
}
else
{
int x,y;scanf("%d%d",&x,&y);
int i=++x;c[i]=0;a[i]=y;
for(;x<=n&&be[x]==be[i];x=x+a[x]) c[i]++;b[i]=x;
for(int j=i-1;be[j]==be[i]&&j>0;j--) if(be[j+a[j]]==be[i]) c[j]=c[j+a[j]]+1,b[j]=b[j+a[j]];
}
}
}
另外吐槽
我恨BZOJ不能下载错误的点