[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;
}

码风清奇,还望包涵~

posted @ 2021-05-18 16:43  letitdown  阅读(64)  评论(0编辑  收藏  举报