[HNOI2010]弹飞绵羊

Posted Brave_Cattle

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HNOI2010]弹飞绵羊相关的知识,希望对你有一定的参考价值。

洛谷题目链接:弹飞绵羊

题目描述

某天,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。

输出格式:

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

输入输出样例
输入样例#1:
4
1 2 1 1
3
1 1
2 1 1
1 1
输出样例#1:
2
3
说明
对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000

稍微解释一下题意:大概就是给出一个长度为n序列,将一只绵羊从指定位置出发,所在位置上面的数字代表它可以向后跳这么多的距离,求出从一个位置出发需要多少次才可以跳出序列。其中有修改操作。

题解:

首先我们想一下,可以通过模拟的方式来推出所需次数,但是这样很显然是会超时的。那么我们应该如何才能优化这个模拟的过程呢?
我们可以用mv[]记录一个元素需要几次才能跳出它所在的块,out[]记录跳到的下一个块上的位置,l[],r[]记录一个块的左右端点,w[]记录向后跳的距离。
那么很显然可以得出每个元素的信息的递推式:

  • 如果一个元素可以跳到下一个块上,则mv[i] = 1 , out[i] = w[i]+i;
  • 如果不能跳到下一个块上,则会跳到下一个位置,此时mv[i] = mv[i+w[i]]+1,out[i] = out[i+w[i]];

然后注意一下要从后面往前推,每次修改一个元素时就重新统计这个元素所在的块就可以了。

#include<bits/stdc++.h>
using namespace std;
const int N=200000+5;
const int inf=2147483647;

int n, m, block, num;
int w[N], b[N];
int l[N], r[N];
int out[N], mv[N];

int gi(){
    int ans = 0 , f = 1; char i = getchar();
    while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();}
    while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();}
    return ans * f;
}

void updata(int x,int y){
    for(int i=y;i>=x;i--)
    if(i+w[i] > r[b[i]]) mv[i] = 1 , out[i] = i+w[i];
    else mv[i] = mv[i+w[i]]+1 , out[i] = out[i+w[i]];
}

int query(int x){
    int pos = x , res = 0;
    while(pos <= n) res += mv[pos] , pos = out[pos];
    return res;
}

int main(){
    //freopen("data.in","r",stdin);
    //freopen("ans.out","w",stdout);
    int x, y, flag; n = gi(); block = sqrt(n);
    for(int i=1;i<=n;i++) w[i] = gi();
    for(int i=1;i<=n;i++) b[i] = (i-1)/block+1;
    for(int i=1;i<=block;i++)
    l[i] = (i-1)*block+1 , r[i] = i*block;
    if(n % block) num = block+1;
    else num = block;
    l[num] = (num-1)*block+1 , r[num] = n;
    m = gi(); updata(1,n);
    for(int i=1;i<=m;i++){
    flag = gi(); x = gi()+1;
    if(flag == 1) printf("%d\n",query(x));
    else y = gi() , w[x] = y , updata(l[b[x]],r[b[x]]);
    }
    return 0;
}

以上是关于[HNOI2010]弹飞绵羊的主要内容,如果未能解决你的问题,请参考以下文章

[HNOI2010]弹飞绵羊

[HNOI2010]弹飞绵羊

HNOI2010 弹飞绵羊

bzoj 2002: [Hnoi2010]Bounce 弹飞绵羊

BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊

2002: [Hnoi2010]Bounce 弹飞绵羊