【bzoj2002】【LCT】弹飞绵羊

3 篇文章 0 订阅
  • 题意:给一段序列和 k[i] 表示从 i 能弹到i+k[i],要求支持动态修改k值,查询由i点几次能被弹出序列长度
  • 数据范围:对于100%的数据n<=200000,m<=100000
  • 题解:首先对于一个点,可以从这个点向 i+k[i] 连一条边, i+k[i]>n 时连向n这个点,显然一个点只向后连一条边,就是一棵树,求得答案就是i节点在以n+1为根的树中的深度
  • 问题转化成了给一个树支持动态修改一条边连的点,求某一点的深度
  • 问题又转化成了LCT裸题
  • 修改先删除再插入

  • 裸题一道,只可惜我太弱了,就因为cut的q p写反了调了一个晚上加一早晨。画了一张纸的树

  • 直接放代码吧 样例过了基本就过了

#include <iostream>
#include <cstdio>
#include <cstring>
#define MAXN 200111
using namespace std;
struct node{
    int size,rev,num;
    node *ch[2],*fa;
    int dir(){return this==fa->ch[1];}
    void cnct(node *p,int d){ch[d]=p;p->fa=this;}
    void up(){size=ch[0]->size+ch[1]->size+1;}
    void down();
}tnull,*null=&tnull,*tree[MAXN];
void node::down(){
    if(this==null) return;
    if(rev){
        rev=0;
        if(ch[0]!=null)ch[0]->rev^=1;
        if(ch[1]!=null)ch[1]->rev^=1;
        swap(ch[0],ch[1]);
    }
}
node* getnode(int u){
    node *p=new node();
    p->size=1;p->rev=0;p->ch[0]=p->ch[1]=p->fa=null;p->num=u;
    return p;
}
bool isroot(node *p){
    return p==null||p->fa->ch[0]!=p&&p->fa->ch[1]!=p;
}
void rot(node *p){
    if(isroot(p)) return;
    int d=p->dir();node *x=p->fa;
    if(x==x->fa->ch[0])
        x->fa->ch[0]=p;
    else if(x==x->fa->ch[1])
        x->fa->ch[1]=p;
    p->fa=x->fa;
    x->cnct(p->ch[!d],d);
    p->cnct(x,!d);
    x->up();
}
void splay(node *p){
    static node *sta[MAXN];int top=0;
    sta[++top]=p;
    for(node *k=p;!isroot(k);k=k->fa)
        sta[++top]=k->fa;
    while(top) sta[top--]->down(); 
    while(!isroot(p)){
        if(isroot(p->fa)) {rot(p);break;}
        else
            if(p->dir()==p->fa->dir()) rot(p->fa),rot(p);
            else rot(p),rot(p);
    }
    p->up();
}
void access(node *p){
    node *q=null;
    while(p!=null){
        splay(p);
        p->cnct(q,1);
        p->up();
        q=p;
        p=p->fa;
    }
}
void mtr(node *p){
    access(p);
    splay(p);
    p->rev=1;
}
void link(node *p,node *q){

    mtr(p);
    p->fa=q;
}
void cut(node *p,node *q){
    mtr(p);
    access(q);
    splay(q);
    q->ch[0]=q->ch[0]->fa=null;
    q->up();
}

int query(node *p,node *q){
    mtr(q);
    access(p);
    splay(p);
    return p->size;
}
int n,m;
int f[MAXN];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n+1;i++)
        tree[i]=getnode(i);
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);f[i]=x+i;
        if(f[i]>n+1) f[i]=n+1;
        link(tree[i],tree[f[i]]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        int a,b,c;
        scanf("%d",&a);
        if(a==1){
            scanf("%d",&b);b++;
            printf("%d\n",query(tree[b],tree[n+1])-1);
        }
        else{
            scanf("%d%d",&b,&c);b++;
            cut(tree[b],tree[f[b]]);
            f[b]=min(b+c,n+1);
            link(tree[b],tree[f[b]]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值