「UOJ2002 BZOJ2002」Bounce 弹飞绵羊【LCT 或 分块】

#2002. [Hnoi2010]Bounce 弹飞绵羊

Description

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

Input

第一行包含一个整数 n n n,表示地上有 n n n个装置,装置的编号从 0 0 0 n − 1 n-1 n1,接下来一行有 n n n个正整数,依次为那 n n n个装置的初始弹力系数。第三行有一个正整数 m m m,接下来 m m m行每行至少有两个数 i 、 j i、j ij,若 i = 1 i=1 i=1,你要输出从 j j j出发被弹几次后被弹飞,若 i = 2 i=2 i=2则还会再输入一个正整数 k k k,表示第 j j j个弹力装置的系数被修改成 k k k。对于 20 % 20\% 20%的数据 n , m ≤ 10000 n,m\leq 10000 n,m10000,对于 100 % 100\% 100%的数据 n ≤ 200000 , m ≤ 100000 n\leq 200000,m\leq100000 n200000,m100000

Output

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

Sample Input

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

Sample Output

2
3

题意

  • 就是给你一个数组 a a a,假如你在一个位置 i i i被弹起来,那么你将落到 i + a [ i ] i+a[i] i+a[i]点处,两种操作,一种是修改 a [ i ] a[i] a[i],另一种是查询从位置 x x x开始跳跳几次后位置第一次 > n >n >n

题解

  • 显然 L C T LCT LCT做法无脑暴力,直接 i i i m i n ( i + a [ i ] , n + 1 ) min(i+a[i],n+1) min(i+a[i],n+1)连边就行了,查询的时候 s p l i t ( x , n + 1 ) split(x,n+1) split(x,n+1),这条实链的 s i z e − 1 size-1 size1就是答案
  • 网上有大佬用分块做的,留坑待填

代码

#include<bits/stdc++.h>

using namespace std;
const int maxn=2e5+10;
#define inf 0x3f3f3f3f

namespace LCT{
    int ch[maxn][2],fa[maxn],mark[maxn];
    int val[maxn],siz[maxn];
    inline bool not_root(int x) {return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
    inline int dir(int x) {return ch[fa[x]][1]==x;}
    inline void add_mark(int x) {swap(ch[x][0],ch[x][1]);mark[x]^=1;}        //将x这颗子树翻转
    inline void push_down(int x) {
        if(mark[x]) {
            if(ch[x][0]) add_mark(ch[x][0]);
            if(ch[x][1]) add_mark(ch[x][1]); 
            mark[x]=0;
        }
    }
    inline void push_up(int x) {
        siz[x]=1;
        if(ch[x][0]) siz[x]+=siz[ch[x][0]];
        if(ch[x][1]) siz[x]+=siz[ch[x][1]];   
    }
    inline void pushall(int x) {
        if(not_root(x)) pushall(fa[x]);
        push_down(x);
    }
    inline void rotate(int x){
        int y=fa[x],z=fa[y],k=dir(x);
        if(ch[x][k^1]) fa[ch[x][k^1]]=y;ch[y][k]=ch[x][k^1];
        if(not_root(y)) ch[z][dir(y)]=x;fa[x]=z;
        ch[x][k^1]=y;fa[y]=x;
        push_up(y);
    }
    inline void splay(int x,int goal=0) {
        pushall(x);
        while(not_root(x)) {
            int y=fa[x],z=fa[y];
            if(not_root(y)) {
                if(dir(x)==dir(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        push_up(x);
    }
    inline void access(int x) {    //从原树的根向x拉一条实链
        for(int y=0;x;y=x,x=fa[x]) {
            splay(x);ch[x][1]=y;push_up(x);
        }
    }
    inline void make_root(int x) {  //使x成为原树的根
        access(x);splay(x);add_mark(x);
    }
    inline int find_root(int x) {   //找到x在原树中的根
        access(x);splay(x);
        while(ch[x][0]) push_down(x),x=ch[x][0];
        splay(x);
        return x;
    }
    inline void split(int x,int y) {   //拉出一条x->y的实链,y为splay根
        make_root(x);access(y);splay(y);
    }
    inline bool link(int x,int y) {    //连接x与y,若已经在同一颗原树中,返回0
        make_root(x);
        if(find_root(y)==x) return 0;
        fa[x]=y;return 1;
    } 
    inline bool cut(int x,int y) {
        make_root(x);
        if(find_root(y)!=x||fa[y]!=x||ch[y][0]) return 0;
        fa[y]=ch[x][1]=0;
        push_up(x);
        return 1;
    }
};
using namespace LCT;

int n,m,opt,a[maxn],x,y;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        if(i+a[i]<=n) link(i,i+a[i]);
        else link(i,n+1);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++) {
        scanf("%d",&opt);
        if(opt==1) {
            scanf("%d",&x);x++;
            split(x,n+1);
            printf("%d\n",siz[n+1]-1);
        }else {
            scanf("%d %d",&x,&y);
            x++;
            if(x+a[x]<=n) cut(x,x+a[x]);
            else cut(x,n+1);
            a[x]=y;
            if(x+a[x]<=n) link(x,x+a[x]);
            else link(x,n+1);
        }
    }
}
~~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值