P3348 [ZJOI2016]大森林(Link-cut-tree)

传送门

题解

题面大意:

(0.)区间加节点

(1.)区间换根

(2.)单点询问距离

如果没有(1)操作,因为区间加节点都是加在下面,所以我们可以直接把(n)棵树压成一棵树,直接询问即可

(1)操作怎么办?

上面挖掘了一点性质,

加节点加在下面,那么我们可以先把节点都加上去,再询问

那么把操作离线,

先按操作位置排序,再按操作排序((0,1)先),再按时间排序

对于(0,1)操作都新建节点

(0)建实点

(1)建虚点

(0)操作的点将连向最后的(1)操作

默认每个(1)操作连向上一个操作(加点直接加在(1)下面)

现在唯一的问题即是(1)操作

我们想一下(pos)转移到(pos+1)

由于一些换根操作

树的形态会发生改变

假如一个换根操作([l,r])

(x)换到(y)

(l-1),根是(x)

(l),根是(y)

那么改变的地方就是把在(x->y)操作之后接上(x)的点,全部接到(y)下面

一个一个挪肯定不行

所以需要一个虚点,挪的话只要挪这一个点

如果没有理解,可以想想,哪些点会连向这个虚点?

一定是时间在它之后的点

没换根之前,这些点都会连向(x)

那么问题就解决了..

Code

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 500010;
int ch[N][2], val[N], sum[N], fa[N], tot;
bool isroot(int x) { return ch[fa[x]][0] != x && ch[fa[x]][1] != x; }
#define get(x) (ch[fa[x]][1] == x)
void pushup(int x) { sum[x] = sum[ch[x][0]] + sum[ch[x][1]] + val[x]; }
void rotate(int x) {
    int y = fa[x], z = fa[y], k = get(x);
    if (!isroot(y)) ch[z][get(y)] = x; fa[x] = z;
    ch[y][k] = ch[x][k ^ 1]; fa[ch[x][k ^ 1]] = y;
    ch[x][k ^ 1] = y; fa[y] = x;
    pushup(y);
}
void splay(int x) {
    while (!isroot(x)) {
        int y = fa[x];
        if (!isroot(y))
            (get(x) ^ get(y)) ? rotate(x) : rotate(y);
        rotate(x);
    }
    pushup(x);
}
int access(int x) {
    int y = 0; for (; x; y = x, x = fa[x]) splay(x), ch[x][1] = y, pushup(x);
    return y;
}
void link(int x, int y) { access(x); splay(x); fa[x] = y; }
void cut(int x) { access(x); splay(x); ch[x][0] = fa[ch[x][0]] = 0; pushup(x); }
void newnode(int x) { val[++tot] = x; sum[tot] = x; }
int L[N], R[N], id[N], len;
struct node {
    int pos, op, x, y;
    bool operator < (const node &z) const {
        return pos == z.pos ?  op < z.op : pos < z.pos;
    }
}q[N];
int ans[N];
int query(int x, int y) {
    int ans = 0, lca;
    access(x), splay(x); ans += sum[x];
    lca = access(y), splay(y), ans += sum[y];
    access(lca), splay(lca), ans -= 2 * sum[lca];
    return ans;
}
int main() {
    int n, m, cnt = 1, last = 2, qs = 0;
    read(n), read(m);
    newnode(1); L[1] = 1, R[1] = n; id[1] = 1;
    newnode(0); link(2, 1);
    for (int i = 1; i <= m; i++) {
        int op; read(op);
        if (!op) {
            int l, r;
            read(l), read(r);
            newnode(1);
            L[++cnt] = l, R[cnt] = r, id[cnt] = tot;
            q[++len] = (node) {1, i - m, tot, last};
        }
        else if (op == 1) {
            int l, r, x;
            read(l), read(r), read(x);
            l = max(l, L[x]), r = min(r, R[x]);
            if (l <= r) {
                newnode(0); link(tot, last);
                q[++len] = (node) {l, i - m, tot, id[x]};
                q[++len] = (node) {r + 1, i - m, tot, last};
                last = tot;
            }          
        }
        else {
            int x, u, v;
            read(x), read(u), read(v);
            q[++len] = (node) {x, ++qs, id[u], id[v]};
        }
    }
	sort(q + 1, q + len + 1);
    int j = 1;
    for (int i = 1; i <= n; i++)
        while (i == q[j].pos && j <= len) {
            if (q[j].op <= 0) cut(q[j].x), link(q[j].x, q[j].y);
            else ans[q[j].op] = query(q[j].x, q[j].y);
			j++;
        }
    for (int i = 1; i <= qs; i++) printf("%d
", ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/zzy2005/p/10644043.html