BZOJ 题目2002: [Hnoi2010]Bounce 弹飞绵羊(link cut tree)

2002: [Hnoi2010]Bounce 弹飞绵羊

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 5421   Solved: 2863
[ Submit][ Status][ Discuss]

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 启发式合并

ac代码

/**************************************************************
    Problem: 2002
    User: kxh1995
    Language: C++
    Result: Accepted
    Time:1912 ms
    Memory:6280 kb
****************************************************************/
 
#include<stdio.h>   
#include<string.h>   
struct LCT  
{  
    int bef[200050],pre[200050],next[200050][2],sum[200050],key[200050];  
    void init()  
    {  
        memset(pre,0,sizeof(pre));  
        memset(next,0,sizeof(next));  
    }
    void pushup(int x)
    {
        sum[x]=key[x]+sum[next[x][1]]+sum[next[x][0]];
    }
    void rotate(int x,int kind)  
    {  
        int y,z;  
        y=pre[x];  
        z=pre[y];  
        next[y][!kind]=next[x][kind];  
        pre[next[x][kind]]=y;  
        next[z][next[z][1]==y]=x;  
        pre[x]=z;  
        next[x][kind]=y;  
        pre[y]=x;  
        pushup(y);
    }  
    void splay(int x)  
    {  
        int rt;  
        for(rt=x;pre[rt];rt=pre[rt]);  
        if(x!=rt)  
        {  
            bef[x]=bef[rt];  
            bef[rt]=0;  
            while(pre[x])  
            {  
                if(next[pre[x]][0]==x)  
                {  
                    rotate(x,1);  
                }  
                else 
                    rotate(x,0);  
            } 
            pushup(x);
        }  
    }  
    void access(int x)  
    {  
        int fa;  
        for(fa=0;x;x=bef[x])  
        {  
            splay(x);  
            pre[next[x][1]]=0;  
            bef[next[x][1]]=x;  
            next[x][1]=fa;  
            pre[fa]=x;  
            bef[fa]=0;  
            fa=x;
            pushup(x);
        }  
    }  
    int query(int x)  
    {  
        access(x);  
        splay(x);  
        // while(next[x][0])  
        //   x=next[x][0];  
        return sum[next[x][0]];  
    }  
    void cut(int x)  
    {  
        access(x);  
        splay(x);  
        bef[next[x][0]]=bef[x];  
        bef[x]=0;  
        pre[next[x][0]]=0;  
        next[x][0]=0;  
    }  
    void join(int x,int y)  
    {  
        if(y==0)  
            cut(x);  
        else 
        {  
            int tmp;  
            access(y);  
            splay(y);  
            for(tmp=x;pre[tmp];tmp=pre[tmp]);  
            if(tmp!=y)  
            {  
                cut(x);  
                bef[x]=y;  
            }  
        }  
    }  
   
}lct;
int a[200020];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        int i;
        lct.init();
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]+i>n)
                lct.bef[i]=0;
            else
                lct.bef[i]=a[i]+i;
            lct.key[i]=lct.sum[i]=1;
        }
        int m;
        scanf("%d",&m);
        while(m--)
        {
            int op;
            scanf("%d",&op);
            if(op==1)
            {
                int x;
                scanf("%d",&x);
                printf("%d\n",lct.query(x+1)+1);
            }
            else
            {
                int x,y;
                scanf("%d%d",&x,&y);
                x++;
                a[x]=y;
                if(x+y>n)
                    //lct.bef[x]=0;
                    y=0;
                else
                    //lct.join(x,y+x);
                    y=y+x;
                lct.join(x,y);
            }
        }
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值