[HNOI2010]弹飞绵羊
蒟弱调了两天终于调出来了。。。
\(n <=200000\),所以可以用到\(n*\sqrt{n}\)的做法,分块。
将\(n\)个器材分为\(\sqrt{n}\)块,维护的是每一个器材弹出此块的步数。
这里用到了\(to\)数组,表示一个器材跳出此块后到达的位置;还用到了\(st\)数组,表示跳出此块需要的步数。
由于维护的步数与位置只在本块内,所以维护时只需要维护同块的即可。
对于修改:
假设我们要修改\(p\)点,那么对于\(p\)后的点是没有影响的,唯一影响到的点就是本块内\(p\)点之前的点,并且这个点加上自己的弹力度还要在\(p\)之前(因为在\(p\)之后的话就不会被影响到了),而且就算在之前,跳跃路程也不一定会经过\(p\),但修改了也无伤大雅(其实是懒得记录影响到的点了
对于修改时,我们需要从\(p\)往前推,这样才能保证能够维护成功,
具体实现请见代码qwq:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
void pn(){printf("\n");}
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 n,pos[200005],a[200005],m,to[200005],st[200005],l[450],r[450];
//pos表示所属块,a表示弹力值,l,r分别表示该块的左右边界
int main()
{
n=read();
for(int i=1;i<=n;i++)a[i]=read();
int t=sqrt(n);
for(int i=1;i<=t;i++){l[i]=r[i-1]+1;r[i]=l[i]+t-1;}
if(t*t<n)t++,l[t]=r[t-1]+1,r[t]=n;
for(int i=1;i<=t;i++)
for(int j=l[i];j<=r[i];j++)
pos[j]=i;
for(int i=n;i>=1;i--)
{
to[i]=i+a[i];
if(to[i]>n)st[i]=1;
else
{
if(pos[i]==pos[to[i]]){st[i]=st[to[i]]+1;to[i]=to[to[i]];}
else st[i]=1;
}
}
m=read();
int p,q,fl,ans;
while(m--)
{
fl=read();
if(fl==1)
{
p=read()+1;
ans=0;
while(p<=n)
{
ans+=st[p];
p=to[p];
}
printf("%d",ans);
pn();
}
else
{
p=read()+1,q=read();
a[p]=q;
to[p]=p+q;
if(to[p]>n)st[p]=1;
else
{
if(pos[p]==pos[to[p]]){st[p]=st[to[p]]+1;to[p]=to[to[p]];}
else st[p]=1;
}
for(int i=p-1;i>=l[pos[p]];i--)
{
if(i+a[i]<=p)
{
st[i]=st[i+a[i]]+1;
to[i]=to[i+a[i]];
}
}
//逆推很重要!!!
}
}
return 0;
}
码风清奇,还望包涵~
Everything that kills me makes me feel alive.