zoukankan      html  css  js  c++  java
  • 【bzoj2002】[Hnoi2010]Bounce 弹飞绵羊 分块/LCT

    题目描述

    某天,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的情况,你都要输出一个需要的步数,占一行。

    样例输入

    4
    1 2 1 1
    3
    1 1
    2 1 1
    1 1

    样例输出

    2
    3


    题解

    写这道题时LCT还不会写,于是码了分块。

    把n个装置分成√n 个块。设p[i]表示从i开始跳出i所在的块后的第一个位置,超过n则为-1;dis[i]为从i到p[i]的次数。

    然后修改只在块内修改,查询只遍历每个块,都只需要√n。

    时间复杂度O(n√n)。

    数据很弱,不需要优化常数,如果TLE,一定是死循环了。

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    int to[200010] , p[200010] , dis[200010];
    inline int read()
    {
    	int num = 0; char ch = getchar();
    	while(ch < '0' || ch > '9') ch = getchar();
    	while(ch >= '0' && ch <= '9') num = (num << 3) + (num << 1) + ch - '0' , ch = getchar();
    	return num;
    }
    int main()
    {
    	int n , i , si , m , opt , x , ans;
    	n = read();
    	si = (int)sqrt(n);
    	for(i = 0 ; i < n ; i ++ )
    		to[i] = read() + i;
    	for(i = n - 1 ; ~i ; i -- )
    	{
    		if(to[i] >= n) p[i] = -1 , dis[i] = 1;
    		else if(to[i] >= (i / si + 1) * si) p[i] = to[i] , dis[i] = 1;
    		else p[i] = p[to[i]] , dis[i] = dis[to[i]] + 1;
    	}
    	m = read();
    	while(m -- )
    	{
    		opt = read() , x = read();
    		if(opt == 1)
    		{
    			ans = 0;
    			for(i = x ; ~i ; i = p[i]) ans += dis[i];
    			printf("%d
    " , ans);
    		}
    		else
    		{
    			to[x] = read() + x;
    			for(i = x ; i >= x / si * si ; i -- )
    			{
    				if(to[i] >= n) p[i] = -1 , dis[i] = 1;
    				else if(to[i] >= (x / si + 1) * si) p[i] = to[i] , dis[i] = 1;
    				else p[i] = p[to[i]] , dis[i] = dis[to[i]] + 1;
    			}
    		}
    	}
    	return 0;
    }

    17.05.02 更新LCT做法

    建立一颗树,每个节点的父节点是它弹一次到达的点,弹出则为n+1(其实可以直接看作森林,然而第一次写保守起见还是建成一棵树)

    于是修改操作就是cut+link,查询就是split。

    另:这题其实不适合当模板题。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 200010
    using namespace std;
    int fa[N] , c[2][N] , si[N] , rev[N] , next[N];
    inline int read()
    {
        int ret = 0; char ch = getchar();
        while(ch < '0' || ch > '9') ch = getchar();
        while(ch >= '0' && ch <= '9') ret = (ret << 3) + (ret << 1) + ch - '0' , ch = getchar();
        return ret;
    }
    void pushup(int k)
    {
        si[k] = si[c[0][k]] + si[c[1][k]] + 1;
    }
    void pushdown(int k)
    {
        if(rev[k])
        {
            int l = c[0][k] , r = c[1][k];
            swap(c[0][l] , c[1][l]) , swap(c[0][r] , c[1][r]);
            rev[l] ^= 1 , rev[r] ^= 1;
            rev[k] = 0;
        }
    }
    bool isroot(int k)
    {
        return c[0][fa[k]] != k && c[1][fa[k]] != k;
    }
    void update(int k)
    {
        if(!isroot(k)) update(fa[k]);
        pushdown(k);
    }
    void rotate(int x)
    {
        int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1;
        if(!isroot(y)) c[c[1][z] == y][z] = x;
        fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y;
        pushup(y) , pushup(x);
    }
    void splay(int x)
    {
        update(x);
        while(!isroot(x))
        {
            int y = fa[x] , z = fa[y];
            if(!isroot(y))
            {
                if((c[0][y] == x) ^ (c[0][z] == y)) rotate(x);
                else rotate(y);
            }
            rotate(x);
        }
    }
    void access(int x)
    {
        int t = 0;
        while(x) splay(x) , c[1][x] = t , pushup(x) , t = x , x = fa[x];
    }
    void makeroot(int x)
    {
        access(x) , splay(x);
        swap(c[0][x] , c[1][x]) , rev[x] ^= 1;
    }
    void link(int x , int y)
    {
        makeroot(x) , fa[x] = y;
    }
    void cut(int x , int y)
    {
        makeroot(x) , access(y) , splay(y) , c[0][y] = fa[x] = 0 , pushup(y);
    }
    int main()
    {
        int n , m , i , opt , x , p;
        n = read();
        for(i = 1 ; i <= n + 1 ; i ++ ) si[i] = 1;
        for(i = 1 ; i <= n ; i ++ ) p = read() , next[i] = min(i + p , n + 1) , link(i , next[i]);
        m = read();
        while(m -- )
        {
            opt = read() , x = read() + 1;
            if(opt == 1) makeroot(n + 1) , access(x) , splay(x) , printf("%d
    " , si[c[0][x]]);
            else p = read() , cut(x , next[x]) , next[x] = min(x + p , n + 1) , link(x , next[x]);
        }
        return 0;
    }
  • 相关阅读:
    越来越火的5G公网专用,到底是啥意思?
    看懂IPv6+,这篇就够了
    今年诺贝尔奖得主居然把这事研究清楚了:学历和收入到底有多大关系?
    华为发布《智能世界2030》报告,多维探索未来十年趋势
    每天一个离职小技巧
    Deepsort + Yolo 实现行人检测和轨迹追踪
    “5G+AI”到底有啥用?这篇漫画告诉你答案…
    全国多地拉闸限电 背后到底怎么了?
    【Ajax请求 】- 1.1前端和后台数据交互的说明
    【Web API系列教程】3.3 — 实战:处理数据(建立数据库)
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6570796.html
Copyright © 2011-2022 走看看