本文主要是介绍BZOJ2002 [Hnoi2010]Bounce 弹飞绵羊 解题报告【数据结构】【分块】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
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
解题报告
这道题要我们支持单调修改。固然我们想要把每个点的答案预处理出来,但因为他需要支持单点修改,所谓牵一发而动全身,一个弹射器的弹力改变了,我们预处理出来的东西势必就要更新。无疑这样太耗时了。我们的解决方法就是,与其预处理出每一个点走出整个地图的答案,不如预处理出每一个点走出一个固定区间的答案。这里的所谓固定区间也就是指分块。
我们用分块预处理出这几个值:
-每个点走出改点所在的分块所需的步数
-每个点被弹射器弹一次到达的坐标,这个主要是方便我们查询的时候顺着这个路径下去
由此,我们写出代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=200000;
int n,m;
int blk,cnt;
int a[N+5],b[N+5],bl[N+5],L[N+5],R[N+5],st[N+5],p[N+5];
int query(int x)//查询被弹飞所需的步数
{int temp=0;while(true){temp+=st[x];if(!p[x])break;x=p[x];}return temp;
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);blk=sqrt(n);if(n%blk)cnt=n/blk+1;else n/blk;for(int i=1;i<=cnt;i++)L[i]=(i-1)*blk+1,R[i]=i*blk;R[cnt]=n;for(int i=1;i<=n;i++)bl[i]=(i-1)/blk+1;for(int i=n;i>=1;i--){if(i+a[i]>n)st[i]=1;elseif(bl[i]==bl[i+a[i]])st[i]=st[i+a[i]]+1,p[i]=p[i+a[i]];//表示在一个块中的弹射器要被弹出该块所需要的步数及其位置的转移情况 else st[i]=1,p[i]=i+a[i];//不在一个块里面 }scanf("%d",&m);while(m--){int opt,x,y;scanf("%d",&opt);if(opt==1){int x;scanf("%d",&x);x++;printf("%d\n",query(x));}else{scanf("%d%d",&x,&y);x++;a[x]=y;for(int i=x;i>=L[bl[x]];i--){if(bl[i]==bl[i+a[i]])st[i]=st[i+a[i]]+1,p[i]=p[i+a[i]];else st[i]=1,p[i]=i+a[i];}}}return 0;
}
这篇关于BZOJ2002 [Hnoi2010]Bounce 弹飞绵羊 解题报告【数据结构】【分块】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!