【HNOI2010】【BZOJ2002】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 启发式合并

直接从i向i+k[i]link一下就行了.
每次修改k记得要先cut原边再加入新边
询问就是维护一下子树size
把弹飞的点都link到n+1上.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 200100
using namespace std;
int n,m;
int k[MAXN];
int sta[MAXN],top;
int opt,u,v;
struct splay
{
    int fa,ch[2],size;
    bool rev;
}tree[MAXN];
inline void in(int &x)
{
    char ch=getchar();int flag=1;x=0;
    while (!(ch>='0'&&ch<='9')) flag=ch=='-'?-1:1,ch=getchar();
    while (ch>='0'&&ch<='9')    x=x*10+ch-'0',ch=getchar();
}
inline bool is_root(int x)
{
    return tree[tree[x].fa].ch[0]!=x&&tree[tree[x].fa].ch[1]!=x;
}
inline void push_down(int x)
{
    if (tree[x].rev)
    {
        tree[x].rev^=1;tree[tree[x].ch[0]].rev^=1;tree[tree[x].ch[1]].rev^=1;
        swap(tree[x].ch[0],tree[x].ch[1]);
    }
}
inline void push_up(int x)
{
    tree[x].size=tree[tree[x].ch[0]].size+tree[tree[x].ch[1]].size+1;
}
inline void rot(int x)
{
    int y=tree[x].fa,z=tree[y].fa,l,r;
    l=(tree[y].ch[1]==x);r=l^1;
    if (!is_root(y))    tree[z].ch[tree[z].ch[1]==y]=x;
    tree[tree[x].ch[r]].fa=y;tree[y].fa=x;tree[x].fa=z;
    tree[y].ch[l]=tree[x].ch[r];tree[x].ch[r]=y;
    push_up(x);push_up(y);
}
inline void Splay(int x)
{
    sta[++top]=x;
    for (int i=x;!is_root(i);i=tree[i].fa)  sta[++top]=tree[i].fa;
    while (top) push_down(sta[top--]);
    while (!is_root(x))
    {
        int y=tree[x].fa,z=tree[y].fa;
        if (!is_root(y))
        {
            if (tree[y].ch[0]==x^tree[z].ch[0]==y)  rot(x);
            else    rot(y);
        }
        rot(x);
    }
}
inline void access(int x)
{
    for (int i=0;x;i=x,x=tree[x].fa)    Splay(x),tree[x].ch[1]=i,push_up(x);
}
inline void make_root(int x)
{
    access(x);Splay(x);tree[x].rev^=1;
}
inline void link(int x,int y)
{
    Splay(x);tree[x].fa=y;
}
inline void cut(int x,int y)
{
    make_root(x);access(y);Splay(y);tree[y].ch[0]=tree[x].fa=0;
}
inline int find(int x)
{
    access(x);Splay(x);
    int ret=x;
    while (tree[ret].ch[0]) ret=tree[ret].ch[0];
    return ret;
}
int main()
{
    in(n);tree[n+1].size=1;
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&k[i]);
        tree[i].size=1;tree[i].fa=min(i+k[i],n+1);
    }
    in(m);
    while (m--)
    {
        in(opt);
        if (opt==1)
        {
            in(u);u++;make_root(n+1);
            access(u);Splay(u);
            printf("%d\n",tree[tree[u].ch[0]].size);
        }
        if (opt==2)
        {
            in(u);in(v);u++;
            cut(u,min(u+k[u],n+1));k[u]=v;link(u,min(u+k[u],n+1));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值