bzoj2002 [Hnoi2010]Bounce 弹飞绵羊 (LCT)

本文介绍了一道名为“Bounce弹飞绵羊”的算法题目,通过构建森林数据结构来解决绵羊弹跳路径的问题。使用Link-Cut树进行高效查询和更新,支持修改弹力系数及查询弹飞次数。

bzoj2002 [Hnoi2010]Bounce 弹飞绵羊

原题地址http://www.lydsy.com:808/JudgeOnline/problem.php?id=2002

题意:
在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。
绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。
有m个操作,每次可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数,或者查询从某个点开始弹几次会被弹飞。

数据范围
n<=200000,m<=100000

题解:
因为每个点只会向后连接一个点,所以最后形成的是一个森林。
如果默认编号更大的点更浅,那么查询一个点弹几次会被弹飞,就是查询这个点到它的根的深度。
那么只需要支持更改父节点的操作和查询点的深度的操作。
就是Link和Cut。
查询深度只需access然后就会有根节点到该点的路径,查询根的size即可。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200005;
int n,m,k[N];
struct node
{
    int fa,ch[2],size;
    bool rez;
    void init()
    {   
        fa=ch[0]=ch[1]=0; size=1;
        rez=0;
    }
}tr[N];
struct Link_Cut_Tree
{
    bool isroot(int x)
    {
        return (tr[tr[x].fa].ch[0]!=x)&&(tr[tr[x].fa].ch[1]!=x);
    }
    void update(int x)
    {
        tr[x].size=1; int ls=tr[x].ch[0]; int rs=tr[x].ch[1];
        if(ls) tr[x].size+=tr[ls].size;
        if(rs) tr[x].size+=tr[rs].size;
        return; 
    }
    void rotate(int x)
    {
        int y=tr[x].fa; int z=tr[y].fa;
        if(!isroot(y))
        {
            if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x;   
        }
        tr[x].fa=z;
        int l= tr[y].ch[0]==x? 0:1; int r=l^1;
        tr[y].ch[l]=tr[x].ch[r]; tr[tr[x].ch[r]].fa=y;
        tr[x].ch[r]=y; tr[y].fa=x;
        update(y); update(x);

    }
    void splay(int x)
    {
        while(!isroot(x))
        {
            int y=tr[x].fa; int z=tr[y].fa;
            if(!isroot(y))
            {
                if(tr[y].ch[0]==x^tr[z].ch[0]==y) rotate(x);
                else rotate(y);
            }
            rotate(x);
        }
    }
    void access(int x)
    {
        int y=0;
        for(;x;y=x,x=tr[x].fa)
        {
            splay(x);
            tr[x].ch[1]=y;
            update(x);
        }
    }
    int findroot(int x)
    {
        access(x);splay(x);
        while(tr[x].ch[0]) x=tr[x].ch[0];
        return x;
    }
    void link(int u,int v)
    {
        if(u>v) swap(u,v); 
        if(findroot(u)==findroot(v)) return;           
        access(u); splay(u);
        tr[u].fa=v;     
    }
    void cut(int u,int v)
    {
        if(u>v) swap(u,v);
        if(findroot(u)!=findroot(v))  return; 

        access(u); splay(u); 
        tr[v].fa=tr[u].ch[0]=0;     
        update(v); update(u);   
        return;
    }
    int query(int x)
    {
        access(x); splay(x);
        return tr[x].size;
    }
}LCT;
int main()
{
     scanf("%d",&n);
     for(int i=0;i<=n;i++) tr[i].init();
     for(int i=1;i<=n;i++)
     {
        scanf("%d",&k[i]);
        int j=i+k[i];
        if(j<=n) LCT.link(i,j);
     }
     scanf("%d",&m);
     while(m--)
     {
        int opt,x,y;
        scanf("%d",&opt);
        if(opt==1)
        {
            scanf("%d",&x); x++;
            printf("%d\n",LCT.query(x));
        }
        else
        {
            scanf("%d%d",&x,&y);
            x++; 
            if(x+k[x]<=n)
            LCT.cut(x,x+k[x]);
            k[x]=y;
            if(x+k[x]<=n)
            LCT.link(x,x+k[x]);            
        }
     }

    return 0;
} 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值