zoukankan      html  css  js  c++  java
  • 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 !!!

  • 相关阅读:
    H
    饭卡(0 1背包)
    J
    H
    E
    3991: [SDOI2015]寻宝游戏
    CF 1051 F. The Shortest Statement
    CF 643 E. Bear and Destroying Subtrees
    4567: [Scoi2016]背单词
    4946: [Noi2017]蔬菜
  • 原文地址:https://www.cnblogs.com/Frank-King/p/9078933.html
Copyright © 2011-2022 走看看