[HNOI2010][BZOJ2002] 弹飞绵羊 - LCT

Posted nishikino-curtis

tags:

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

Description

某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

Input & Output

Input

第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1。

接下来一行有n个正整数,依次为那n个装置的初始弹力系数。

第三行有一个正整数m,

接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。

Output

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

Sample

Input

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

Output

2
3

Solution

裸LCT,似乎分块可做?蒟蒻表示没想出来……首先如何表示被弹飞,在n之外开一个点即可,比如n+1,如果i + k_i > n,那么就link一下这两个点,需要注意的是cut同理。
为了方便操作,把标号统一+1s,各种姿势都会比较方便。查询的时候就split(x, n + 1),这样查询的区间大小实际上就是从x到n+1经过的点数,答案就是size[n + 1] - 1。
Code:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cctype>

using std::min;
using std::max;
using std::swap;
using std::isdigit;

const int maxn = 200005;

struct NODE
{
    int siz, rev, c[2];
    NODE()
    {
        siz = rev = c[2] = 0;
    }
}t[maxn];
int n,m,k[maxn],f[maxn];
int opt,x,y;

inline int rd()
{
    int x = 0;char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x;
}
void pushdown(int x)
{
    if(t[x].rev && x)
    {
        t[t[x].c[0]].rev ^= 1;
        t[t[x].c[1]].rev ^= 1;
        swap(t[x].c[0], t[x].c[1]);
        t[x].rev = 0;
    }
}
void pushup(int x)
{
    t[x].siz = t[t[x].c[0]].siz + t[t[x].c[1]].siz + 1;
}
int getid(int x)
{
    return t[f[x]].c[1] == x;
}
int isroot(int x)
{
    return t[f[x]].c[1] != x && t[f[x]].c[0] != x;
}
void rotate(int x)
{
    int fa = f[x], fa_ = f[fa], k = getid(x);
    if(!isroot(fa))
        t[fa_].c[t[fa_].c[1] == fa] = x;
    t[fa].c[k] = t[x].c[k ^ 1]; f[t[fa].c[k]] = fa;
    t[x].c[k ^ 1] = fa;
    f[fa] = x; f[x] = fa_;
    pushup(fa); pushup(x);
}
void pushpath(int x)
{
    if(!isroot(x)) pushpath(f[x]);
    pushdown(x);
}
void splay(int x)
{
    pushpath(x);
    for(int fa; !isroot(x); rotate(x))
    {
        if(!isroot(fa = f[x]))
            rotate(getid(x) == getid(fa) ? fa : x);
        pushup(x);
    }
}
void access(int x)
{
    for(int y = 0; x; y = x, x = f[x])
        splay(x), t[x].c[1] = y, pushup(x);
}
void makeroot(int x)
{
    access(x); splay(x); t[x].rev ^= 1;
}
int findroot(int x)
{
    access(x); splay(x);
    while(t[x].c[0]) x = t[x].c[0];
    return x;
}
void split(int x,int y)
{
    makeroot(x); access(y); splay(y);
}
void link(int x,int y)
{
    makeroot(x);
    if(findroot(y) != x) f[x] = y;
}
void cut(int x,int y)
{
    split(x, y); t[y].c[0] = f[x] = 0; pushup(y);
}

int main()
{
    n = rd();
    for(int i = 1; i <= n; ++i)
    {
        k[i] = rd();
        if(i + k[i] > n) link(i, n + 1);
        else link(i, i + k[i]);
    }
    m = rd();
    for(int i = 1; i <= m; ++i)
    {
        opt = rd();
        if(opt == 1)
        {
            x = rd();
            x += 1;
            split(x, n + 1); 
            printf("%d\n",t[n + 1].siz - 1);
        }
        else 
        {
            x = rd(); y = rd();
            x += 1;
            if(x + k[x] > n)cut(x, n + 1);
            else cut(x, x + k[x]);
            k[x] = y;
            if(x + k[x] > n)link(x, n + 1);
            else link(x, x + k[x]);
        }
    }
    return 0;
}

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

BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊

bzoj2002HNOI2010Bounce 弹飞绵羊

[bzoj2002][Hnoi2010]Bounce弹飞绵羊_LCT

bzoj 2002 : [Hnoi2010]Bounce 弹飞绵羊 (LCT)

BZOJ2002:[HNOI2010]弹飞绵羊——题解

bzoj2002 [Hnoi2010]Bounce 弹飞绵羊