BZOJ2002[Hnoi2010]弹飞绵羊——LCT

本文详细解析了Lostmonkey设计的游戏算法,通过一种超级弹力装置模拟游戏过程,探讨了使用LCT(链接切割树)进行高效路径查询和更新的方法。在特定条件下,文章提供了完整的C++代码实现,展示了如何处理节点连接、修改和查询操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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

输入

第一行包含一个整数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

输出

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

样例输入

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

样例输出

2
3
 
当跳过n之后就被弹飞,那么可以建一个虚点n+1,将弹飞看作弹到n+1这个节点上。
i会被弹到i+ki号节点上,那么直接将i号节点和i+ki号节点连上就好了。
修改直接断开原来连的边,重新连接一条边,LCT维护就好了。
因为每个点只会连接一个比它编号大的点,所以最后形成了一棵树,每次询问直接查询j和n+1路径上的节点数-1即可。
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
int x,y;
int opt;
int s[200020][2];
int f[200020];
int r[200020];
int st[200020];
int size[200020];
int dis[200020];
int get(int rt)
{
    return rt==s[f[rt]][1];
}
void pushup(int rt)
{
    size[rt]=size[s[rt][0]]+size[s[rt][1]]+1;
}
void pushdown(int rt)
{
    if(r[rt])
    {
        r[s[rt][0]]^=1;
        r[s[rt][1]]^=1;
        r[rt]^=1;
        swap(s[rt][0],s[rt][1]);
    }
}
int is_root(int rt)
{
    return rt!=s[f[rt]][0]&&rt!=s[f[rt]][1];
}
void rotate(int rt)
{
    int fa=f[rt];
    int anc=f[fa];
    int k=get(rt);
    if(!is_root(fa))
    {
        s[anc][get(fa)]=rt;
    }
    s[fa][k]=s[rt][k^1];
    f[s[fa][k]]=fa;
    s[rt][k^1]=fa;
    f[fa]=rt;
    f[rt]=anc;
    pushup(fa);
    pushup(rt);
}
void splay(int rt)
{
    int top=0;
    st[++top]=rt;
    for(int i=rt;!is_root(i);i=f[i])
    {
        st[++top]=f[i];
    }
    for(int i=top;i>=1;i--)
    {
        pushdown(st[i]);
    }
    for(int fa;!is_root(rt);rotate(rt))
    {
        if(!is_root(fa=f[rt]))
        {
            rotate(get(fa)==get(rt)?fa:rt);
        }
    }
}
void access(int rt)
{
    for(int x=0;rt;x=rt,rt=f[rt])
    {
        splay(rt);
        s[rt][1]=x;
        pushup(rt);
    }
}
void reverse(int rt)
{
    access(rt);
    splay(rt);
    r[rt]^=1;
}
int find(int rt)
{
    access(rt);
    splay(rt);
    while(s[rt][0])
    {
        rt=s[rt][0];
    }
    return rt;
}
void link(int x,int y)
{
    reverse(x);
    f[x]=y;
}
void cut(int x,int y)
{
    reverse(x);
    access(y);
    splay(y);
    s[y][0]=f[x]=0;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n+1;i++)
    {
        size[i]=1;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        dis[i]=x;
        if(i+x<=n)
        {
            link(i,i+x);
        }
        else
        {
            link(i,n+1);
        }
    }
    scanf("%d",&m);
    while(m--)
    {
        scanf("%d",&opt);
        if(opt==1)
        {
            scanf("%d",&x);
            x++;
            reverse(x);
            access(n+1);
            splay(n+1);
            printf("%d\n",size[n+1]-1);
        }
        else
        {
            scanf("%d%d",&x,&y);
            x++;
            if(x+dis[x]<=n)
            {
                cut(x,x+dis[x]);
            }
            else
            {
                cut(x,n+1);
            }
            dis[x]=y;
            if(x+dis[x]<=n)
            {
                link(x,x+dis[x]);
            }
            else
            {
                link(x,n+1);
            }
        }
    }
}

转载于:https://www.cnblogs.com/Khada-Jhin/p/9744804.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值