VEXOBEN
Vexoben
Mar 2, 2019
It takes 11 minutes to read this article.

题目链接:ZJOI2016 大森林

一道题调了一年,码力还是太弱了。

(刷新以获取数学公式)

题意

棵树,每棵树初始有一个节点.设计一个数据结构,支持:

0、在第棵树的生长节点下面挂一个孩子.如果这是第个0操作,那么孩子节点的编号为.初始每棵树的生长节点都是1.

1、将第棵树的生长节点置为.如果一棵树没有节点,那么不更改.

2、询问第棵树上点和点的距离.

数据范围:

题解

同时维护棵树的结构显然不现实.但是我们可以离线后求出每棵树最终的样子.显然在最终的树上进行查询还是在中间的树上查询不会影响答案.

对于每个1操作,我们建立一个虚点;对于每个操作,我们建立一个实点.一个实点对应的虚点是上一个建立的虚点(一开始的生长节点是,相当于插入了一个虚点)

遍历每棵树.每个实点和虚点都有一个生效的区间.当一个实点生效时,我们将它连接到它对应的虚点;当它失效时,cut这条边.当一个虚点生效时,将它连接到对应的实点上;当它失效时,将它连接到上一个虚点上.

可以这样考虑正确性:只要每个实点都在正确的位置上就可以了.它本身如果失效,那他没有任何连边.如果它和它连接的虚点都生效,显然是正确的.如果它生效,而它连接的虚点失效,它会随着虚点之间的边连到生效的最早的点上,从而保证了,将每个点的父亲连接到它第一个非虚点的祖先后,形成的树和原树相同.

需要注意的是,统计答案的时候不能直接求两点路径上实点的数目.因为如果LCA是虚点,它其实对应它第一个非虚点的祖先,从而导致少算了的贡献,求一下LCA特判即可.

LCT求LCA的方法:先,再,答案就是最后一次轻重边转换的位置,具体可以看代码的部分.

#pragma GCC optimize("2,Ofast,inline")
#include<bits/stdc++.h>
using namespace std;
const int N = 4e5 + 10;

namespace LCT {
	int V, fa[N], val[N], sum[N], rev[N], ch[N][2];

	int Nw(int v) {
		++V;
		val[V] = sum[V] = v;
		return V;
	}
	
	int Isroot(int x) {
		return (ch[fa[x]][0] != x) && (ch[fa[x]][1] != x);
	}

	int Get(int x) {
		return (ch[fa[x]][1] == x);
	}

	inline void Pushup(int x) {
		sum[x] = sum[ch[x][0]] + sum[ch[x][1]] + val[x];
	}

	inline void Pushdown(int x) {
		if (rev[x]) {
			rev[x] = 0;
			rev[ch[x][0]] ^= 1;
			rev[ch[x][1]] ^= 1;
			swap(ch[x][0], ch[x][1]);
		}
	}

	void Rotate(int x) {
		int y = fa[x], z = fa[y], k = Get(x);
		fa[x] = z;
		if (!Isroot(y)) ch[z][Get(y)] = x;
		ch[y][k] = ch[x][k ^ 1];
		fa[ch[x][k ^ 1]] = y;
		ch[x][k ^ 1] = y;
		fa[y] = x;
		Pushup(y); Pushup(x);
	}

	void Splay(int x) {
		static int top, st[N];
		st[top = 1] = x;
		for (int i = x; !Isroot(i); i = fa[i]) {
			st[++top] = fa[i];
		}
		while (top) Pushdown(st[top--]);
		for (; !Isroot(x); Rotate(x)) {
			if (!Isroot(fa[x])) {
				Get(x) == Get(fa[x]) ? Rotate(fa[x]) : Rotate(x);
			}
		}
	}

	int Access(int x) {
		int i;
		for (i = 0; x; i = x, x = fa[x]) {
			Splay(x);
			ch[x][1] = i;
			Pushup(x);
		}
		return i;
	}

	void Evert(int x) {
		Access(x); Splay(x); rev[x] ^= 1;
	}

	void Link(int x, int y) {
		Evert(x); Access(y); Splay(y);
		fa[x] = y;
	}

	void Cut(int x, int y) {
		Evert(x); Access(y); Splay(y);
		fa[x] = ch[y][0] = 0;
		Pushup(y);
	}

	int Query(int x, int y) {
		int ans = 0;
		Evert(1);
		Access(x); Splay(x);
		ans += sum[x];
		int lca = Access(y); Splay(y);
		ans += sum[y];
		Access(lca); Splay(lca);
		ans -= 2 * sum[lca];
		return ans;
	}

}

void read(int &x) {
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	for (x = 0; c >= '0' && c <= '9'; c = getchar())
		x = (x << 3) + (x << 1) + c - '0';
}

int q, n, m, rv, las, tot, L[N], R[N], num[N], ans[N];

struct ope {
	int x, opt, ins, pre, pos, node;
	bool operator < (const ope &b) const {
		return pos < b.pos;
	}
}a[N];

struct que {
	int u, v, ord;
};
vector<que> vec[N];

int main() {
	cin >> n >> m;
	num[++rv] = LCT::Nw(1);
	las = LCT::Nw(0);
	LCT::Link(num[1], las);
	L[1] = 1; R[1] = n;
	for (int i = 1; i <= m; ++i) {
		int opt, l, r, x;
		read(opt);
		if (opt == 0) {
			read(l); read(r);
			x = LCT::Nw(1);
			num[++rv] = x;
			L[rv] = l; R[rv] = r;
			a[++tot] = (ope) {x, 0, 1, las, l};
			a[++tot] = (ope) {x, 0, -1, las, r + 1};
		}
		if (opt == 1) {
			read(l); read(r); read(x);
			l = max(l, L[x]);
			r = min(r, R[x]);
			if (l <= r) {
				int t = LCT::Nw(0);
				a[++tot] = (ope) {num[x], 1, 1, las, l, t};
				a[++tot] = (ope) {num[x], 1, -1, las, r + 1, t};
				LCT::Link(t, las);
				las = t;
			}
		}
		if (opt == 2) {
			read(x); read(l); read(r);
			vec[x].push_back((que) {l, r, ++q});
		}
	}
	sort(a + 1, a + tot + 1);
	for (int i = 1, p = 1; i <= n; ++i) {
		while (p <= tot && a[p].pos == i) {
			if (a[p].opt == 0) {
				if (a[p].ins == 1) {
					LCT::Link(a[p].x, a[p].pre);
				}
				if (a[p].ins == -1) {
					LCT::Cut(a[p].x, a[p].pre);
				}
			}
			if (a[p].opt == 1) {
				if (a[p].ins == 1) {
					LCT::Cut(a[p].node, a[p].pre);
					LCT::Link(a[p].node, a[p].x);
				}
				if (a[p].ins == -1) {
					LCT::Cut(a[p].node, a[p].x);
					LCT::Link(a[p].node, a[p].pre);
				}
			}
			++p;
		}
		for (int j = 0; j < vec[i].size(); ++j) {
			int x = vec[i][j].u, y = vec[i][j].v;
			ans[vec[i][j].ord] = LCT::Query(num[x], num[y]);
		}
	}
	for (int i = 1; i <= q; ++i) {
		printf("%d\n", ans[i]);
	}
	return 0;
}