【bzoj2002】[Hnoi2010]Bounce 弹飞绵羊 动态树

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

HINT

Source

Splay 启发式合并


Splay启发式合并什么鬼…………

据说分块能水?

i 个点向第i+num[i]个点连边

很容易想到是一个树形结构,需要新建一个根节点root代表弹飞后指向的点。

设根节点深度为0,那每个点的答案就是它的深度。

支持修改:改变一个点的父亲。

一个link+一个cut即可。

代码:

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;

const int SZ = 1000010;
const int INF = 1000000010;

struct node{
    node *f,*ch[2];
    int sz;
    bool rev;

    void setc(node *x,int d) { (ch[d] = x) -> f = this; }

    int dir() { return f -> ch[1] == this; }

    bool isroot() { return f -> ch[0] != this && f -> ch[1] != this; }

    void maintain() { sz = ch[0] -> sz + 1 + ch[1] -> sz; }

    void pushdown();
}T[SZ], *null, *tree[SZ], *S[SZ];

int Tcnt = 0;

node* newnode()
{
    node *k = T + (Tcnt ++);
    k -> ch[0] = k -> ch[1] = k -> f = null;
    k -> sz = 1;
    k -> rev = 0;
    return k;
}

void pushrev(node *p) { if(p == null) return ; swap(p -> ch[0],p -> ch[1]); p -> rev ^= 1; }

void node :: pushdown()
{
    if(rev) { pushrev(ch[0]); pushrev(ch[1]); rev = 0; }
}

void rotate(node *p)
{
    int d = p -> dir();
    node *fa = p -> f;
    p -> f = fa -> f;
    if(!fa -> isroot()) fa -> f -> ch[fa -> dir()] = p;
    fa -> ch[d] = p -> ch[d ^ 1];
    if(fa -> ch[d] != null) fa -> ch[d] -> f = fa;
    p -> setc(fa, d ^ 1);
    fa -> maintain(); p -> maintain();
} 

int top = 0;

void pushpath(node *p)
{
    while(!p -> isroot()) S[++ top] = p,p = p -> f;
    S[++ top] = p;
    while(top) S[top --] -> pushdown();
}

void splay(node *p)
{
    pushpath(p);
    while(!p -> isroot())
    {
        p -> maintain();
        if(p -> f -> isroot()) rotate(p);
        else
        {
            if(p -> dir() == p -> f -> dir()) rotate(p -> f),rotate(p);
            else rotate(p),rotate(p);
        }
    }
    p -> maintain();
}

void access(node *p)
{
    node *last = null;
    while(p != null)
    {
        splay(p);
        p -> ch[1] = last; p -> maintain();
        last = p;
        p = p -> f;
    }
}

void toroot(node *p)
{
    access(p);
    splay(p);
    pushrev(p);
}

void link(node *u,node *v)
{
    toroot(u);
    u -> f = v;
}

void cut(node *u,node *v)
{
    toroot(u);
    access(v);
    splay(v);
    v -> ch[0] = u -> f = null;
}

int n;

int ask(node *p)
{
    toroot(tree[n + 1]);
    access(p);
    splay(p);
    return p -> sz;
}

int num[SZ];

void init() { null = newnode(); null -> sz = 0; }

int main()
{
    init();
    scanf("%d",&n);
    for(int i = 1;i <= n + 1;i ++) tree[i] = newnode();

    for(int i = 1;i <= n;i ++) 
    {
        scanf("%d",&num[i]);
        link(tree[i],tree[min(n + 1,i + num[i])]);
    }

    int m;
    scanf("%d",&m);
    while(m --)
    {
        int opt;
        scanf("%d",&opt);
        if(opt == 1) 
        {
            int pos;
            scanf("%d",&pos);
            printf("%d\n",ask(tree[pos + 1]) - 1);
        }
        else
        {
            int pos,x;
            scanf("%d%d",&pos,&x);
            pos ++;
            cut(tree[pos],tree[min(n + 1,pos + num[pos])]);
            num[pos] = x;
            link(tree[pos],tree[min(n + 1,pos + num[pos])]);
        }
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值