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 !!!

  • 相关阅读:
    web前端的发展态势
    AngularJs 简单入门
    css代码优化篇
    git提交报错:Please make sure you have the correct access rights and the repository exists.
    Activiti工作流框架学习
    遍历map集合的4种方法
    js设置日期、月份增加减少
    Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
    webservice_rest接口_学习笔记
    相互匹配两个list集合+动态匹配${}参数
  • 原文地址:https://www.cnblogs.com/Frank-King/p/9078933.html
Copyright © 2011-2022 走看看