BZOJ 2002 Hnoi2010 弹飞绵羊 分块

题意

某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。

游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。

绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。

为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

n200000

分析

正解LCT。
但是可以考虑分块。

我们将位置分成 n 块,对于每一块进行维护。
每一块我们需要保存以下几个值:
wij :第 i 块第j个位置能往前跳多少个位置;
lastij :第 i 块第j个位置在本块跳到的最后位置;
cntij :第 i 块第j个位置跳到本块最后一个位置需要多少步;

这样可以解决询问操作:
假设从 x 开始跳,首先x last 跳到块尾,答案累加用 cnt ,再用 w 数组将x跳到块尾位置跳到的下一个位置,直到跳出。

现在只用解决维护就可以了。

最初的时候,我们需要求这三个的初始值。
由于每一块的取值不受外界影响,所以我们每一块分开来求,使用函数 Calculate(i) 表示求第 i 块。
从块内的最后一个元素往前递推,分跳到块内、跳到块外处理。

后来的更改,只用暴力找到位置更改了,然后整块用Calculate重新求一次。

代码

#include <cstdio>
#include <cctype>
#include <cmath>
#include <algorithm>
using namespace std;

const int N=200010;
const int BLOCK_SIZE=500;
const int BLOCK_MAX=500;

int n;

int unit,tot;
int w[BLOCK_SIZE][BLOCK_MAX];
int cnt[BLOCK_SIZE][BLOCK_MAX];
int last[BLOCK_SIZE][BLOCK_MAX];

inline int Read(void)
{
    int x=0,f=1; char c=getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

void Calculate(int nb)
{
    int bnum=min(unit,n-(nb-1)*unit); int gb,gl;
    for (int i=bnum;i>=1;i--)
        if (i+w[nb][i]>bnum)
        {
            cnt[nb][i]=1;
            last[nb][i]=i;
        }
        else
        {
            cnt[nb][i]=cnt[nb][i+w[nb][i]]+1;
            last[nb][i]=last[nb][i+w[nb][i]];
        }
}

void Init(void)
{
    n=Read();   
    unit=(int)sqrt(n);

    int cur=unit;
    for (int i=1;i<=n;i++)
    {
        if (cur==unit) tot++,cur=0;
        w[tot][++cur]=min(Read(),n+1);
    }

    for (int i=1;i<=tot;i++) Calculate(i);
}

int m;

int Query(int x)
{
    int nb,nl,sum=0;
    for (;x<=n;)
    {
        nb=(x-1)/unit+1,nl=x-(nb-1)*unit;
        sum+=cnt[nb][nl];
        x=(nb-1)*unit+last[nb][nl]+w[nb][last[nb][nl]];
    }
    return sum;
}

void Update(int x,int y)
{
    int nb=(x-1)/unit+1,nl=x-(nb-1)*unit;
    w[nb][nl]=y;
    Calculate(nb);
}

void Work(void)
{
    int k,x,y; m=Read();
    for (int i=1;i<=m;i++)
    {
        k=Read();
        if (k==1)
        {
            x=Read()+1;
            printf("%d\n",Query(x));
        }
        else
        {
            x=Read()+1,y=Read();
            Update(x,y);
        }
    }
}

int main(void)
{
    Init();
    Work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值