[NOI2021] 轻重边

话说NOI前一天才看到过原题。

考虑转换,令端点颜色相同的边为原题中的重边,则先给所有点染上不同的颜色。考虑修改,显然这一条链上的边都需要是重边,因此这一条链都要染成一颜色,而与它相邻的边变成轻边了,所以这条链上的点的颜色和其它点的颜色都不同。我们直接把这条链染成一种没出现过的颜色即可。

查询就变成了链上多少条边两端颜色相同,易知点数-极长连续颜色段数就是答案。而颜色段数我们维护一个最左边的颜色,最右边的颜色和当前答案就容易求出了。

不知道为什么那么多人用树剖,明明 LCT 更好写。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>

using namespace std;

const int N = 2e5 + 5;

int ch[N][2], val[N], sum[N], fa[N], siz[N], n, m, tag[N], lc[N], rc[N], col[N], T, st[N], top;

inline int read() {
	register int s = 0;
	register char ch = getchar();
	while (!isdigit(ch)) ch = getchar();
	while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
	return s;
}

#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define isRight(x) (ch[fa[x]][1]==x)

inline bool nroot(int x) { return ls(fa[x]) == x || rs(fa[x]) == x; }

inline void pushdown(int x) {
	if (tag[x]) {
		swap(ls(x), rs(x));
		swap(lc[ls(x)], rc[ls(x)]); swap(lc[rs(x)], rc[rs(x)]);
		tag[ls(x)] ^= 1; tag[rs(x)] ^= 1;
		tag[x] = 0;
	}
	if (col[x]) {
		lc[x] = rc[x] = val[x] = col[x];
		col[ls(x)] = col[rs(x)] = col[x];
		col[x] = sum[x] = 0;
	}
}

inline void update(int x) {
	pushdown(ls(x)); pushdown(rs(x));
	siz[x] = siz[ls(x)] + siz[rs(x)] + 1;
	sum[x] = sum[ls(x)] + sum[rs(x)];
	if (ls(x)) {
		lc[x] = lc[ls(x)];
		if (val[x] != rc[ls(x)]) ++sum[x];
	} else lc[x] = val[x];
	if (rs(x)) {
		rc[x] = rc[rs(x)];
		if (val[x] != lc[rs(x)]) ++sum[x];
	} else rc[x] = val[x];
}

inline void rotate(int x) {
	int f = fa[x]; int ff = fa[f], c = isRight(x); if (nroot(f)) ch[ff][isRight(f)] = x;
	ch[f][c] = ch[x][c ^ 1]; fa[ch[x][c ^ 1]] = f; ch[x][c ^ 1] = f;
	fa[f] = x; fa[x] = ff; update(f); update(x);
}

inline void splay(int x) {
	top = 0; st[++top] = x; for (register int i = x; nroot(i); i = fa[i]) st[++top] = fa[i];
	while (top) pushdown(st[top--]);
	for (register int f = fa[x]; nroot(x); rotate(x), f = fa[x])
		if (nroot(f)) rotate((isRight(f) ^ isRight(x)) ? x : f);
	update(x);
}

inline void access(int x) { for (register int i = 0; x; x = fa[i = x]) splay(x), rs(x) = i, update(x); }

inline int getroot(int x) { access(x); splay(x); while (ls(x)) pushdown(x), x = ls(x); splay(x); return x; }

inline void makeroot(int x) { access(x); splay(x); tag[x] ^= 1; }

inline void link(int x, int y) { if (getroot(x) != getroot(y)) makeroot(x), fa[x] = y; }

inline void split(int x, int y) { makeroot(x); access(y); splay(y); }

inline void modify(int x, int y, int z) { split(x, y); col[y] = z; }

inline int query(int x, int y) { split(x, y); return siz[y] - 1 - sum[y]; }

int main() {
	scanf("%d", &T);
	while (T--) {
		n = read(); m = read();
		for (register int i = 1; i <= n; ++i) val[i] = lc[i] = rc[i] = i, sum[i] = siz[i] = 1;
		register int opt, x, y, cl = n;
		for (register int i = 1; i < n; ++i) {
			x = read(); y = read(); link(x, y);
		}
		while (m--) {
			opt = read(); x = read(); y = read();
			if (opt == 1) modify(x, y, ++cl);
			else printf("%d\n", query(x, y));
		}
		memset(val, 0, sizeof val); memset(lc, 0, sizeof lc); memset(rc, 0, sizeof rc);
		memset(ch, 0, sizeof ch); memset(sum, 0, sizeof sum); memset(fa, 0, sizeof fa);
		memset(siz, 0, sizeof siz); memset(tag, 0, sizeof tag); memset(col, 0, sizeof col);
	}
	return 0;
}
posted @ 2021-08-10 17:55  Smallbasic  阅读(126)  评论(0)    收藏  举报