[BZOJ4573][[Zjoi2016]大森林][LCT建虚点]

50 篇文章 0 订阅
5 篇文章 0 订阅

[BZOJ4573][[Zjoi2016]大森林][LCT建虚点]

题意懒得写了。。。。

思路:

建LCT的时候我们可以引入虚点。对于所有的1操作,新建一个没有权值的虚点,然后对于0操作,可以把新建的节点挂在最后建的虚点上面。由于虚点并不参与权值的计算,所以我们可以把所有操作先离线,然后从左到右把所有树都做一遍,每做到一个操作的时候,把在当前树不存在的虚点(当前树不在某个修改生长节点的操作的影响范围内)都断开,然后连到这个虚点出现之前的虚点上(也就是本该属于这颗树的生长节点)。

然后打个lct就好了。

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

const int Maxn = 200010;
inline int Max(const int &a, const int &b) {
    return a > b ? a : b;
}
inline int Min(const int &a, const int &b) {
    return a < b ? a : b;
}
inline char get(void) {
    static char buf[100000], *p1 = buf, *p2 = buf;
    if (p1 == p2) {
        p2 = (p1 = buf) + fread(buf, 1, 100000, stdin);
        if (p1 == p2) return EOF;
    }
    return *p1++;
}

inline void read(int &x) {
    x = 0; static char c;
    for (; !(c >= '0' && c <= '9'); c = get());
    for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get());
}

inline void write(int x) {
    if (!x) return (void) (puts("0"));
    static short st[12], top;
    while (x) st[++top] = x % 10, x /= 10;
    while (top) putchar('0' + st[top--]);
    putchar('\n');
}

struct Op {
    int pos, op, x, y;
    Op(void) {}
    Op(const int &pos, const int &op, const int &x, const int &y) : pos(pos), op(op), x(x), y(y) {}
    friend bool operator < (const Op &a, const Op &b) {
        if (a.pos == b.pos) return a.op < b.op;
        else return a.pos < b.pos; 
    }
} a[Maxn << 2];

int c[Maxn][2], size[Maxn], val[Maxn], fa[Maxn];
inline void pushUp(int x) {
    int l = c[x][0], r = c[x][1];
    size[x] = size[l] + size[r] + val[x];
}
inline bool Rt(int x) {
    return c[fa[x]][0] != x && c[fa[x]][1] != x;
}

inline void rotate(int x) {
    int y = fa[x], z = fa[y], l = (c[y][1] == x), r = l ^ 1;
    if (!Rt(y)) {
        if (c[z][0] == y) c[z][0] = x;
        else c[z][1] = x;
    }
    fa[y] = x; fa[x] = z; fa[c[x][r]] = y;
    c[y][l] = c[x][r]; c[x][r] = y; 
    pushUp(y); pushUp(x);
}
inline void splay(int x) {
    while (!Rt(x)) {
        int y = fa[x], z = fa[y];
        if (!Rt(y)) {
            if (c[y][0] == x ^ c[z][0] == y) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}

inline int access(int x) {
    int t;
    for (t = 0; x; t = x, x = fa[x])
        splay(x), c[x][1] = t, pushUp(x);
    return t;
}

inline void cut(int x) {
    access(x), splay(x), c[x][0] = fa[c[x][0]] = 0; pushUp(x);
}
inline void link(int x, int y) {
    splay(x); fa[x] = y;
}
int p, cnt, m, n, tot, L[Maxn], R[Maxn], id[Maxn], now, ans[Maxn];
inline void add(int o) {
    size[n + 1] = val[++n] = o;
}
int main(void) {
    //freopen("in.txt", "r", stdin);
    read(p), read(m);
    add(1); cnt = 1; L[cnt] = id[cnt] = 1; R[cnt] = p;
    add(0); now = 2; link(2, 1);
    int op, k, x, y;
    memset(ans, -1, sizeof ans);
    for (int i = 1; i <= m; i++) {
        read(op); 
        if (op == 0) {
            read(x), read(y); ++cnt;
            L[cnt] = x, R[cnt] = y, add(1), id[cnt] = n;
            a[++tot] = Op(1, i - m, n, now);
        } else if (op == 1) {
            read(x), read(y), read(k);
            x = Max(x, L[k]), y = Min(y, R[k]);
            if (x <= y) {
                add(0);
                if (x > 1) link(n, now);
                a[++tot] = Op(x, i - m, n, id[k]);
                a[++tot] = Op(y + 1, i - m, n, now);
                now = n;
            }
        } else {
            read(k), read(x), read(y);
            a[++tot] = Op(k, i, id[x], id[y]);
        }
    }
    sort(a + 1, a + tot + 1);
    for (int i = 1, k = 1; i <= p; i++) 
        for (; k <= tot && a[k].pos == i; k++) {
        if (a[k].op > 0) {
            access(a[k].x), splay(a[k].x), ans[a[k].op] = size[a[k].x];
            int t = access(a[k].y); splay(a[k].y), ans[a[k].op] += size[a[k].y];
            access(t), splay(t), ans[a[k].op] -= size[t] << 1;
        } else {
            cut(a[k].x), link(a[k].x, a[k].y);
        }
    }
    for (int i = 1; i <= m; i++)
        if (ans[i] != -1) {
            write(ans[i]);
        } 
    return 0;
}

完。

By g1n0st

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值