BZOJ2002 [Hnoi2010]Bounce 弹飞绵羊 解题报告【数据结构】【分块】

本文介绍了一道涉及游戏模拟的算法题目,通过分块预处理技术实现高效查询与单点更新,支持弹射器弹力系数的动态修改。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
        else
        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];//不在一个块里面 
    }
    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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值