BZOJ4573: [Zjoi2016]大森林 (LCT)

传送门

题解:
好题啊。

我们直接从左往右离线做。
问题就变成了动态插入,删除0,1操作。

明显插入删除1操作都是将连续的一段0换父亲, 我们可以建虚点来维护, 每个连续段的虚点彼此相连,插入则改变与前面的连接即可。

用LCT维护,时间复杂度 O(nlogn) O ( n log ⁡ n )

#include <bits/stdc++.h>
using namespace std;

const int RLEN=1<<18|1;
inline char nc() {
    static char ibuf[RLEN], *ib, *ob;
    (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob) ? -1 : *ib++;
}

inline int rd() {
    char ch=nc(); int i=0, f=1;
    while(!isdigit(ch)) {if(ch=='-') f=-1; ch=nc();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
    return i*f;
}

inline void W(int x) {
    static int buf[50];
    if(!x) {putchar('0'); return;}
    if(x<0) {putchar('-'); x=-x;}
    while(x) {buf[++buf[0]]=x%10; x/=10;}
    while(buf[0]) {putchar(buf[buf[0]--]+'0');}
}
const int N=2e5+50;

int n, m, cnt=1, tot; 
int L[N], R[N];

struct node {
    node *lc, *rc, *fa;
    int v, sum;
    inline void upt() {
        sum=lc->sum+rc->sum+v;
    }
}Pool[N], *pool=Pool, *null=Pool, *vir_fa[N], *ori_fa[N], *pos[N], *pos2[N], *pre[N], *lst;

inline node* newnode(int v) {
    node *t=++pool;
    t->lc=t->rc=t->fa=null;
    t->v=t->sum=v;
    return t;
}

inline bool isroot(node *x) {return x->fa->lc!=x && x->fa->rc!=x; }
inline bool which(node *x) {return x->fa->lc==x;}

inline void rotate(node *x) {
    node *y=x->fa, *z=y->fa;
    if(!isroot(y)) ((z->lc==y)?z->lc:z->rc)=x;
    x->fa=z; y->fa=x;
    if(y->lc==x) {
        node *b=x->rc;
        x->rc=y; y->lc=b;
        if(b!=null) b->fa=y;
    } else {
        node *b=x->lc;
        x->lc=y; y->rc=b;
        if(b!=null) b->fa=y;
    } y->upt(); x->upt();
}
inline void splay(node *x) {
    while(!isroot(x)) {
        node *y=x->fa;
        if(!isroot(y)) {
            if(which(x)==which(y)) rotate(y);
            else rotate(x);
        } rotate(x);
    }
}

inline node* access(node *x) {
    node *t=null;
    for(node *y=null; x!=null; y=x, x=x->fa) {
        t=x; splay(x);
        x->rc=y; x->upt();
    }
    return t;
}

inline void link(node *x, node *f) {
    splay(x); x->fa=f;
}

inline void cut(node *x, node *f) {
    access(f); 
    splay(x);
    x->fa=null;
}

struct data { int op, x, id; };
vector <data> query_s[N];

int ans[N];

int main() {
    memset(ans, -1 ,sizeof(ans));
    pos[1]=newnode(1); 
    lst=pos[1];
    n=rd(), m=rd();
    L[1]=1; R[1]=n;
    for(int i=1; i<=m; i++) {
        int op=rd();
        if(!op) {
            int l=rd(), r=rd(), x=++cnt;
            vir_fa[x]=lst;
            pos[x]=newnode(1);
            L[x]=l, R[x]=r;
            query_s[l].push_back((data){1,x,i});
            query_s[r+1].push_back((data){-1,x,i});
        } else if(op==1){
            int l=rd(), r=rd(), x=rd();
            l=max(l, L[x]), r=min(r, R[x]);
            if(l>r) continue;
            ++tot; 
            pos2[tot]=newnode(0);
            pre[tot]=lst;
            lst=pos2[tot];
            ori_fa[tot]=pos[x];
            query_s[l].push_back((data){2,tot,i});
            query_s[r+1].push_back((data){-2,tot,i});
        } else {
            int p=rd(), u=rd(), v=rd();
            query_s[p].push_back((data){u+3,v+3,i});
        }
    }
    for(int i=1; i<=tot; i++) 
        link(pos2[i], pre[i]);
    for(int i=1; i<=n; i++) {
        for(int e=0; e<query_s[i].size(); e++) {
            data u=query_s[i][e];
            if(u.op>2) {
                int x=u.op-3, y=u.x-3;
                access(pos[y]);
                int v=(splay(pos[y]), pos[y]->sum);
                node *t=access(pos[x]);
                v+=(splay(pos[x]), pos[x]->sum);
                v-=(access(t), splay(t), 2*t->sum);
                ans[u.id]=v;
            } else if(u.op==1) {
                link(pos[u.x], vir_fa[u.x]);
            } else if(u.op==-1) {
                cut(pos[u.x], vir_fa[u.x]);
            } else if(u.op==2) {
                cut(pos2[u.x], pre[u.x]);
                link(pos2[u.x], ori_fa[u.x]);
            } else {
                cut(pos2[u.x], ori_fa[u.x]);
                link(pos2[u.x], pre[u.x]);
            }
        }
    }
    for(int i=1; i<=m; i++) {
        if(~ans[i]) W(ans[i]), putchar('\n');
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值