BZOJ 2002 [Hnoi2010]Bounce 弹飞绵羊 (LCT)

一棵有根树,支持两种操作:

1,查询指定节点深度

2,为指定节点换父亲


既然树形态改变了,那么就把一切都交给LCT吧。。

LCT的关键函数access()只有10行,但是加上了splay,调起来还是作死的节奏啊,吐血2天终于AC了

关于link-cut tree的复杂度,虽然不会证,但我能造出的极限数据是n个节点的完美二叉树,然后相邻两个询问是两个路径尽量不相同的叶子节点,使splay的功效降到最低,每次都要从叶子一步一步走到根,单次logn,那么就是nlogn的复杂度了。但是对于splay本身巨大的常数只能说一声:呵呵

这里的splay只是作为载体而存在,所以简化了一些操作过程,也因此失去了形如o->ch[1]->ch[0]这样提取区间的功能,但还是能够很方便的做到提取一段链

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <time.h>
using namespace std;

const int N = 202000;
int n;

struct Node *nill;

struct Node {
        Node *ch[2],*fa;
        int sz;
        bool d() { return fa->ch[1]==this; }
        bool is() { return fa==nill || fa->ch[0]!=this && fa->ch[1]!=this; }
        void setc(Node *o,int c) {
                ch[c] = o;
                o->fa = this;
                up();
        }
        void up() {
                if (this==nill) return ;
                sz = ch[0]->sz + ch[1]->sz + 1;
        }
        void rot() {
                int c = d();
                Node *tmp = fa;
                tmp->setc(ch[c^1],c);
                if (tmp->is()) fa = tmp->fa;
                else tmp->fa->setc(this,tmp->d());
                setc(tmp,c^1);
                fa->up();
        }
        void splay() {
                while (!is()) {
                        if (!fa->is()) {
                                d()==fa->d() ? fa->rot() : rot();
                        }
                        rot();
                }
        }
        void access() {
                Node *p = this,*q = nill;
                while (p!=nill) {
                        p->splay();
                        p->setc(q,1);
                        q = p;
                        p = p->fa;
                }
                splay();
        }

}memo[N],*idx[N],*bat;
void change(Node *a,Node *b) {
        a->splay();
        a->ch[0]->fa = a->fa;
        a->setc(nill,0);
        a->fa = b;
}
void newNode(Node *&o) {
        o = bat ++;
        o->ch[0] = o->ch[1] = o->fa = nill;
        o->sz  =1;
}
void init() {
        bat = memo;
        newNode(nill); nill->sz = 0;
}
void show(Node *o) {
        if (o==nill) return ;
        show(o->ch[0]);
        printf("%d ",o-memo);
        show(o->ch[1]);
}
inline void read(int &x) {
        char c; bool sig = 0;
        for (c = getchar(); c<'0' || c>'9'; c = getchar()) if (c=='-') sig = 1;
        for (x = c-'0',c = getchar(); c>='0' && c<='9'; c = getchar()) x = x*10+c-'0';
        sig && (x=-x);
}
int main() {
        //     freopen("in.txt","r",stdin);
        while (scanf("%d",&n)==1) {
                init();
                for (int i = 0; i <= n; i ++) {
                        newNode(idx[i]);
                }
                for (int i = 0; i < n; i ++) {
                        int x;
                        read(x);
                        idx[i]->fa = idx[min(n,i+x)];
                }
                int nq;
                read(nq);
                while (nq--) {
                        int op,a,b;
                        read(op); read(a);
                        if (op==1) {
                                idx[a]->access();
                                printf("%d\n",idx[a]->sz - 1);
                        } else {
                                read(b);
                                b = min(n,a+b);
                                change(idx[a],idx[b]);
                        }
                }
        }
        //        printf("%.6f\n",(double)clock()/CLOCKS_PER_SEC);
        return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值