【LCT】BZOJ2002(Hnoi2010)[Bounce 弹飞绵羊]题解

39 篇文章 2 订阅
9 篇文章 0 订阅

题目概述

有n个弹力装置,每个弹力装置i弹力系数为ki,会弹到i+ki,如果不存在i+ki则被弹飞,给出m个操作/询问,操作:将某个装置的弹力系数改变,询问:询问从某个弹力装置开始弹几次会被弹飞。

解题报告

加入一个n+1点表示弹飞,那么每个弹力装置就只会有一个目的地,但一个目的地可能会有很多儿子,所以是一棵树,用LCT进行维护。每次询问的话只需要将x->n+1的路径拿出来,求路径长度(节点个数-1)即可。

示例程序

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=200000;

int n,te,K[maxn+5];
struct LCT
{
    int father[maxn+5],son[maxn+5][2],si[maxn+5];
    bool flip[maxn+5];
    void Pushup(int p) {si[p]=si[son[p][0]]+1+si[son[p][1]];}
    void Add_flip(int p) {swap(son[p][0],son[p][1]);flip[p]^=1;}
    void Pushdown(int p)
    {
        if (!flip[p]) return;
        if (son[p][0]) Add_flip(son[p][0]);
        if (son[p][1]) Add_flip(son[p][1]);
        flip[p]=false;
    }
    bool is_ro(int p) {return p!=son[father[p]][0]&&p!=son[father[p]][1];}
    void Rotate(int p)
    {
        int fa=father[p],d=p==son[fa][0];
        if (!is_ro(fa)) son[father[fa]][fa==son[father[fa]][1]]=p;
        son[fa][d^1]=son[p][d];father[son[p][d]]=fa;son[p][d]=fa;
        Pushup(fa);Pushup(p);father[p]=father[fa];father[fa]=p;
    }
    int top,stk[maxn+5];
    void Splay(int p) //用非递归Splay比较方便,省去了找p的过程
    {
        stk[++top]=p;
        for (int i=p;!is_ro(i);i=father[i]) stk[++top]=father[i];
        while (top) Pushdown(stk[top--]); //因为非递归,所以要先将上面的点传标记
        while (!is_ro(p))
        {
            int fa=father[p];
            if (!is_ro(fa))
            {
                int d1=fa==son[father[fa]][1],d2=p==son[fa][1];
                if (d1==d2) Rotate(fa); else Rotate(p);
            }
            Rotate(p);
        }
    }
    void Access(int p)
    {
        int lst=0;
        while (p)
        {
            Splay(p);son[p][1]=lst;Pushup(p);
            lst=p;p=father[p];
        }
    }
    void make_ro(int p) {Access(p);Splay(p);Add_flip(p);}
    void Link(int x,int y) {Access(y);make_ro(y);father[y]=x;}
    void Cut(int x,int y)
    {
        make_ro(y);Access(x);Splay(x);
        father[y]=son[x][0]=0;Pushup(x);
    }
};
LCT tr;

bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x)
{
    int tot=0,f=1;char ch=getchar(),lst='+';
    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}
    if (lst=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
    x=tot*f;
    return Eoln(ch);
}
void First_init() {for (int i=1;i<=n+1;i++) tr.si[i]=1;}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    readi(n);First_init();
    for (int i=1;i<=n;i++) readi(K[i]),tr.Link(min(i+K[i],n+1),i); //初始边
    readi(te);
    while (te--)
    {
        int td,x,y;readi(td);readi(x);x++;
        if (td==1)
        {
            tr.make_ro(n+1);tr.Access(x);tr.Splay(x);
            printf("%d\n",tr.si[x]-1);
        } else
        readi(y),tr.Cut(min(x+K[x],n+1),x),tr.Link(min(x+(K[x]=y),n+1),x);
        //和原来的目的地断开,连接到新的目的地上
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值