[HNOI2010]弹飞绵羊——[LCT]

本文介绍了一个弹力游戏的模拟算法,使用线性结构和链截断树实现绵羊游戏过程的模拟,支持实时修改弹力系数和查询弹射次数。

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

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

【输入格式】
第一行包含一个整数nnn,表示地上有nnn个装置,装置的编号从000n−1n-1n1
接下来一行有nnn个正整数,依次为那nnn个装置的初始弹力系数。

第三行有一个正整数mmm

接下来mmm行每行至少有两个数iiijjj,若i=1i=1i=1,你要输出从jjj出发被弹几次后被弹飞,若i=2i=2i=2则还会再输入一个正整数kkk,表示第jjj个弹力装置的系数被修改成k。

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

Sample  InputSample\;InputSampleInput

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

Sample  OutputSample\;OutputSampleOutput

2
3

【题意分析】

一个坑点:编号从0到n-1

题目保证每只绵羊只会往后弹,弹到后面没有了就直接弹飞。那么对于被弹飞,可以理解成弹到了第n+1n+1n+1个点。(虚构的一个节点)

输入的时候,设第xxx个点设置成往后弹a[x]a[x]a[x],那么如果x+a[x]>nx+a[x] > nx+a[x]>nlink (x,x+a[x])link~(x,x+a[x])link (x,x+a[x]),否则link (x,n+1)link~(x,n+1)link (x,n+1)(被弹飞)

操作的时候,如果是修改,先判断要修改的点连的是n+1n+1n+1还是x+a[x]x+a[x]x+a[x],先cutcutcut,然后跟输入时差不多,判断之后再linklinklink。别忘了修改劲度系数!!(此处WA*1)
如果是询问,那就access (n+1)access~(n+1)access (n+1)splay (n+1)splay~(n+1)splay (n+1),然后输出size[n+1]−1size[n+1]-1size[n+1]1

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cctype>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#define MAXN 500000
using namespace std;

int son[MAXN][2],father[MAXN],size[MAXN],lazy[MAXN];
int a[MAXN],stack[MAXN],n,q;

namespace Link_Cut_Tree{
    
    inline bool isroot (int x){
        return !(son[father[x]][0] == x || son[father[x]][1] == x);
    }//是否为根
    
    inline void pushup (int x){
        size[x] = 1;
        if (son[x][0])size[x] += size[son[x][0]];
        if (son[x][1])size[x] += size[son[x][1]];
    }//更新size
    
    inline void pushdown (int x){
        if (lazy[x]){
            swap (son[x][0],son[x][1]);
            if (son[x][0])lazy[son[x][0]] ^= 1;
            if (son[x][1])lazy[son[x][1]] ^= 1;
            lazy[x] = 0;
        }
    }
    
    inline void rotate (int x){
        int y = father[x],z = father[y];
        int k = son[y][1] == x,kk = son[z][1] == y;
        if (!isroot (y))son[z][kk] = x;
        father[x] = z;
        son[y][k] = son[x][k^1];
        father[son[x][k^1]] = y;
        son[x][k^1] = y;
        father[y] = x;
        pushup (y); pushup (x);
    }
    
    inline void splay (int x){
        int top = 0;
        stack[++top] = x;
        for (register int i = x;!isroot (i);i = father[i])
            stack[++top] = father[i];
        for (register int i = top;i;i--)
            pushdown (stack[i]);
        while (!isroot (x)){
            int y = father[x],z = father[y];
            if (!isroot (y))
                (son[y][1] == x) ^ (son[z][1] == y)
                    ? rotate (x) : rotate (y);
            rotate (x);
        }
        pushup (x);
    }
    
    inline void access (int x){
        for (register int y = 0;x;y = x,x = father[x]){
            splay (x);
            son[x][1] = y;
            pushup (x);
        }
    }
    
    inline int findroot (int x){
        access (x);
        splay (x);
        while (son[x][0]){
            pushdown (son[x][0]);
            x = son[x][0];
        }
        return x;
    }
    
    inline void makeroot (int x){
        access (x);
        splay (x);
        lazy[x] ^= 1;
    }
    
    inline void link (int x,int y){
        makeroot (x);
        father[x] = y;
    }
    
    inline void cut (int x,int y){
        makeroot (x);
        access (y);
        splay (y);
        son[y][0] = father[x] = 0;
        pushup (y);
    }
    
    inline int query (int x,int y){
        makeroot (x);
        access (y);
        splay (y);
        return size[y] - 1; 
    }
    
}using namespace Link_Cut_Tree;

int main (){
    scanf ("%d",&n);
    for (register int i = 1;i <= n+1;i++)
        size[i] = 1;
    for (register int i = 1;i <= n;i++){
        int x;
        scanf ("%d",&x);
        (i+x <= n)
            ? link (i,i+x) : link (i,n+1);
        a[i] = x;
    }
    scanf ("%d",&q);
    while (q--){
        int type;
        scanf ("%d",&type);
        if (type == 1){
            int x;
            scanf ("%d",&x); x++;
            printf ("%d\n",query (x,n+1));
        }
        if (type == 2){
            int x,y;
            scanf ("%d%d",&x,&y); x++;
            (x+a[x] <= n)
                ? cut (x,x+a[x]) : cut (x,n+1);
            (x+y <= n)
                ? link (x,x+y) : link (x,n+1);
            a[x] = y;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值