BZOJ 弹飞绵羊 【分块入门】

BZOJ2002[HNOI2010]弹飞绵羊

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
 
 

题解

表示不会LCT(Link-Cut Tree) ,索性用分块水过去(逃~)

个人认为此题分块还算巧妙,(分块算法的变种还是蛮多的,出题人想怎么玩就怎么玩!!!)

块的大小还是套路 √n ,(本人较懒,不想优化,dalao勿喷)

 用分块主要是优化跳跃式地找到下一个跳到的位置。(这里有点像并查集,每一个块就是一个集,还有路径压缩)

对于第i位置,如果 i+k[i] 的位置仍在此块内,则直接将 i+k[i] 的状态转到 i 上(即:step数组表示步数,pos数组表示跳到的位置,那么 step[i]==step[i+k[i]]+1, pos[i]==pos[i+k[i]]; )

否则如果跳到下一个块或更后面的块,就直接把位置记到 i+k[i] 上。

最后一个挺重要的一点就是,状态要逆着推,即循环是从右往左的,这样才能保证状态的传递,延续(即实现路径压缩)。

 

不多说了,直接上代码(丑陋,dalao请忽略):

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
int n,k[200005],m,cnt,step[200005],pos[200005],belong[200005],range;
int l[1005],r[1005];
int calc(int x)
{
    int ans=0;
    while (true)
    {
        ans+=step[x];
        if (!pos[x]) break;
        x=pos[x];
    }
    return ans;
}
void change(int x,int y)
{
    for (int i=x; i>=l[belong[x]]; i--)
    {
        if (belong[i]==belong[i+k[i]])
        {
            step[i]=step[i+k[i]]+1;
            pos[i]=pos[i+k[i]];
        }
        else step[i]=1,pos[i]=i+k[i];
    }
}
int main()
{
    scanf("%d",&n); range=sqrt(n);
    for (int i=1; i<=n; i++)
    {
      scanf("%d",&k[i]);
      belong[i]=(i-1)/range+1;
    }
    cnt=(n-1)/range+1;
    for (int i=1; i<=cnt; i++)
      l[i]=(i-1)*range+1,r[i]=i*range;
    for (int i=n; i>=1; i--)
    {
        if (i+k[i]>n) step[i]=1;
        else if (belong[i]==belong[i+k[i]])
          step[i]=step[i+k[i]]+1,pos[i]=pos[i+k[i]];
        else step[i]=1,pos[i]=i+k[i];
    }
    scanf("%d",&m);
    for (int i=1; i<=m; i++)
    {
        int num,x1,k1;
        scanf("%d%d",&num,&x1);
        x1++;
        if (num==1) printf("%d",calc(x1));
        else { scanf("%d",&k1); k[x1]=k1; change(x1,k1); }
    }
    return 0;
}
View Code

 

本人第一次写题解,往dalao多多指教,在下感激不尽。

以后继续加油!!!fighting fighting fighting !!!

posted on 2018-05-23 19:46  Frank-King  阅读(256)  评论(2编辑  收藏  举报