【BZOJ-2002】[HNOI2010] Bounce 弹飞绵羊

题目链接

题目描述

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

题解

容易发现(显然)从一个弹簧只会被弹到一个确定的地方,但一个地方可能会从不同的弹簧弹过来。不觉得这是一棵树么,弹到哪个点上,哪个点就是它的父亲节点。我们新建一个点表示弹出去了,那么次数不就是出发点到这个点的树上距离吗?
再来看看更换弹力系数实际上干了些啥,不就是给他换了个父亲吗。
于是成了 LCT 裸题,动态询问树上两点间的距离。

/*
  LCT 求一个点到根的距离方法:
  先access(p),Splay(p) 这样p的左子树就是所有在p点上方的点,维护size即可;
 */
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
#include<vector>
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();int t=1;
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    return x*t;
}
#define __ NULL
#define ls son[0]
#define rs son[1]
#define get_son(a) (a->fa->rs==a)
#define get_size(a) (a==__? 0:a->size)
const int N=2e5+20;
struct node{
    node* fa;node* son[2];int k;
    bool rev,is_root;int size;//要维护size 
    void clear(){fa=ls=rs=__;rev=0;is_root=1;size=1;k=0;}
}*tr[N];
node pool[N];int cnt;
node* st[N];int h;
inline void updata(node* p){p->size=1+get_size(p->ls)+get_size(p->rs);}
inline void push_down(node* p)
{
    if(p==__||p->rev==0) return;
    p->rev=0;
    if(p->ls!=__)p->ls->rev^=1;
    if(p->rs!=__)p->rs->rev^=1;
    swap(p->ls,p->rs);
}
inline void rotate(node* p)
{
    if(p==__||p->is_root==1) return;
    register int k=get_son(p);
    register node* q=p->fa;register node* gp=q->fa;
    q->son[k]=p->son[k^1];
    if(p->son[k^1]!=__) p->son[k^1]->fa=q;p->fa=gp;
    if(q->is_root) q->is_root=0,p->is_root=1;
    else gp->son[get_son(q)]=p;
    q->fa=p;p->son[k^1]=q;
    updata(p);updata(q);
}
inline void Splay(node* p)
{
    if(p==__)return;
    h=1;
    st[h]=p;
    for(register node* i=p;!i->is_root;st[++h]=(i=i->fa));
    while(h) push_down(st[h--]);
    for(;!p->is_root;rotate(p)){
        if(p->fa->is_root) {rotate(p);break;}
        if(get_son(p->fa)==get_son(p)) rotate(p->fa);
        else rotate(p);
    }
    updata(p);
}
inline void access(node* p){
    for(register node* pre=__;p!=__;pre=p,p=p->fa){
        Splay(p);
        if(p->rs!=__) p->rs->is_root=1;
        p->rs=pre;if(pre!=__) pre->is_root=0;updata(p);
    }
}
inline void m_root(node* p){access(p);Splay(p);p->rev^=1;}
inline void Link(node* p,node* q){m_root(p);p->fa=q;}
inline void split(node* p,node* q){m_root(p);access(q);Splay(q);}
inline void Cut(node* p,node* q) {
    split(p,q);
    if(q->ls==p) p->fa=q->ls=__,p->is_root=1,updata(q);
}
int n;
inline void Query(node* p)
{
    split(tr[n+1],p);
    printf("%d\n",get_size(p->ls));
}
int main()
{
    n=read();
    for(register int i=1;i<=n;i++) tr[i]=&pool[cnt++],tr[i]->clear(),tr[i]->k=read();
    tr[n+1]=&pool[cnt++];tr[n+1]->clear();
    for(register int i=1;i<=n;i++){
        register int nt=tr[i]->k+i;
        if(nt>n) nt=n+1;Link(tr[i],tr[nt]);
    }
    int m=read();register int op;register int x,y;
    for(register int i=1;i<=m;i++){
        op=read();
        if(op==1) Query(tr[read()+1]);
        else {
            x=read()+1;y=read();
            register int nt1=tr[x]->k+x;
            if(nt1>n) nt1=n+1;
            register int nt2=y+x;
            if(nt2>n) nt2=n+1;
            if(nt1==nt2) continue;
            tr[x]->k=y;
            Cut(tr[x],tr[nt1]);
            Link(tr[x],tr[nt2]);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值