luoguP3203 [HNOI2010]BOUNCE 弹飞绵羊

P3203 [HNOI2010]BOUNCE 弹飞绵羊

题目描述

某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

输入输出格式

输入格式:

第一行包含一个整数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

输出格式:

对于每个i=1的情况,你都要输出一个需要的步数,占一行。

输入输出样例

输入样例#1:
4
1 2 1 1
3
1 1
2 1 1
1 1
输出样例#1:
2
3

 思路:

  分块什么的

坑点:

  1.注意是从0号元素开始!!!

  2.在luogu上最好不要用next数组...

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;

const int M = 200000;
const int N = 1000;
int n,m;
int k[M],steps[M],nxt[M];
///step数组存的是需要多少步跳出当前块
///next数组存的是跳到(下一块)的下一个点 
int len,ks,kuai[M],l[N],r[N];///分块 

void Builds()
{
    ///块的长度
    len=sqrt(n);
    ///块的个数 
    ks=len;
    if(n%ks) ks++;
    ///分块 
    for(int i=0;i<n;i++)
        kuai[i]=(i-1)/len;
    /*
        因为块数是从0号开始计数的,
        当i==(√n)时如果不进行-1,
        会被分到第一块,
        但事实上它是第0块的,
        所以进行-1 
    */ 
    for(int i=1;i<=ks;i++)
    {
        l[i]=(i-1)*len;
        ///r[i]=i*len-1中-1的原因是:
        ///因为数组是从0号开始储存的 
        r[i]=i*len-1;
    }
    ///将最后一块的右端手动设置为n的长度 
    r[ks]=n;
    for(int i=n-1;i>=0;i--)
    {
        int now=i+k[i];
        ///如果下一步跳到的地方已经跳出最远的距离n的话 
        if(now>=n)
            ///更新步数:跳一步就跳出去 
            steps[i]=1,
            ///这里重要!需要手动将next数组设置一个不可能出现的数值 
                nxt[i]=-1;
        else 
        ///假如在同一块中 
            if(kuai[i]==kuai[now])
            ///更新步数,i的步数由now的步数多跳一步来到当前(点)位置 
                steps[i]=steps[now]+1,
                ///更新接下来跳到的点是... 
                    nxt[i]=nxt[now];
        else
        ///不在一块中 
            steps[i]=1,
            ///跳到now这个点 
                nxt[i]=now; 
    }
}

int ask(int pre)
{
    int ans=0;
    while(pre!=-1)
    {
        ans+=steps[pre];
        pre=nxt[pre];
    }
    return ans;
 } 

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&k[i]);
    ///开始分块 
    Builds();
    scanf("%d",&m);
    for(int i=0,c;i<m;i++)
    {
        scanf("%d",&c);
        if(c==1)
        {
            int pre;
            scanf("%d",&pre);
            printf("%d
",ask(pre)); 
        }
        else 
        {
            int pre,ki;
            scanf("%d%d",&pre,&ki);
            ///更新k值 
            k[pre]=ki;
            ///因为更改k之后只会影响到当前块,并且只会影响他前面的 
            for(int j=pre;j>=l[kuai[pre]];j--)
            {
                int now=j+k[j];
                if(now>=n)
                    steps[j]=1,
                        nxt[j]=-1;
                else 
                    if(kuai[j]==kuai[now])
                        steps[j]=steps[now]+1,
                            nxt[j]=nxt[now];
                else
                    steps[j]=1, 
                        nxt[j]=now; 
            }
        }
    }
    return 0;
}
View Code

如果运气好也是错,那我倒愿意错上加错!

❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀

原文地址:https://www.cnblogs.com/zxqxwnngztxx/p/7125274.html