[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;
}