[HNOI2010]弹飞绵羊

LCT的板子题

蒟蒻看到很多大佬用分块的方法解决了这道题,但是本蒟蒻不会分块,但我会LCT啊,所以就用LCT解决了这道题。

神奇的思路

对于每一个节点 维护他的size,每一个弹射器 由它即将到达的弹射器向他连边

虚拟一个n+1号节点 进行最后一次弹射,
每次直接查询对应位置的size即可。

也就是

把给出的数据当做森林,即i的父亲是i+k[i],询问的是到根的路径长度+1,可以修改父亲。所以只需要link操作即可,注意把超过n的节点设为0。每次询问先access,然后Splay到根(Splay中要维护size),这样所询问的答案就是节点在Splay上的左子树大小+1。

CODE:

// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>

using namespace std;

const int maxn = 200010;

inline int read(){
    int x = 0;
    int flag = true;
    int k = getchar();
    while(k != '-' && !isdigit(k))
        k = getchar();
    if(k == '-'){
       k = getchar();
       flag = false;
    }
    while(isdigit(k)){
        x = x * 10 + k - '0';
        k = getchar();
    }
    return (flag ? x : -x);
}

struct node{
    int father;
    int ch[2];
    int size;
    bool is_root;
}tree[maxn];

int n,m,k[maxn];

void update(int x){
    tree[x].size=1;
    if(tree[x].ch[0]) tree[x].size += tree[tree[x].ch[0]].size;
    if(tree[x].ch[1]) tree[x].size += tree[tree[x].ch[1]].size;
}
inline int getson(int x){
    return x == tree[tree[x].father].ch[1];
}
void rotate(int x){
    if(tree[x].is_root) return;
    int k = getson(x),fa = tree[x].father;
    int fafa = tree[fa].father;
    tree[fa].ch[k] = tree[x].ch[k^1];
    if(tree[x].ch[k^1])
        tree[tree[x].ch[k^1]].father = fa;
    tree[x].ch[k^1] = fa;
    tree[fa].father = x;
    tree[x].father = fafa;
    if(!tree[fa].is_root)
        tree[fafa].ch[fa == tree[fafa].ch[1]] = x;
    else {
        tree[x].is_root = true;
        tree[fa].is_root = false;
    }
    update(fa);
    update(x);
}
void Splay(int x){
    for(int y ; !tree[x].is_root;rotate(x)){
        if(!tree[y = tree[x].father].is_root){
            rotate((getson(x) == getson(y)) ? y : x);
        }
    }
}
void access(int x){
    int y = 0;
    while(x){
        Splay(x);
        tree[tree[x].ch[1]].is_root = true;
        tree[tree[x].ch[1] = y].is_root = false;
        update(x);
        x = tree[y = x].father;
    }
}
void link(int u,int v){
    if(v > n) v = 0;
    access(u);
    Splay(u);
    tree[tree[u].ch[0]].is_root = true;
    tree[tree[u].ch[0]].father = 0;
    tree[u].ch[0] = 0;
    tree[u].father = v;
    update(u);
}
int Query(int x){
    access(x);
    Splay(x);
    return tree[tree[x].ch[0]].size + 1;
}

int main(){
    n = read();
    for(int i = 1 ; i <= n ; i++){
        tree[i].is_root = 1;
        tree[i].size = 1;
        tree[i].father = 0;
        tree[i].ch[0] = 0;
        tree[i].ch[1] = 0;
    }
    for(int i = 1 ; i <= n ; i++){
        k[i] = read();
        link(i,i + k[i]);
    }
    m = read();
    int xx,x,y;
    while(m--){
        xx = read();
        x = read();
        if(xx == 1)
            printf("%d\n",Query(x+1));
        else if(xx == 2){
            y = read();
            link(x + 1,x + y + 1);
        }
    }
    return 0;
}

另外,LCT的时间复杂度是\(O(logn^2)\),所以不开氧气优化也能过。
蒟蒻的耗时为652ms。

转载于:https://www.cnblogs.com/Repulser/p/9600779.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值