前言
笑點解析:特殊性質 \(t\) 判錯了,掛了 \(15\)pts。
題意簡述
一棵 \(n\) 個結點的有根樹,\(1\) 為樹根,初始每個點都有不同的顏色。在接下來 \(m\) 次操作中,你需要維護這棵樹,有如下 \(3\) 種操作:
- 把 \(u\) 到根這條鏈上每一個點的顏色覆蓋為新的一種顏色;
- 查詢 \(f(u, v)\),其中 \(f(u, v)\) 表示 \(u\) 到 \(v\) 鏈上不同顏色數;
- 對於一個 \(u\) 子樹中的 \(v\),查詢 \(\max \limits _ {v \in \operatorname{subtree}(u)} f(1, v)\)。
\(n, m \leq 2 \times 10 ^ 5\)。
題目分析
由於每次覆蓋都是新開一個顏色,所以我們猜想 \(f(u, v)\) 和 \(u\) 到 \(v\) 鏈上顏色段數是相同的。
點選展開證明
考慮一種顏色,最初其在樹上出現總是一條鏈,且這條鏈的一端為另一端的祖先。考慮影響到這種顏色,當且僅當另一種顏色把鏈頂到鏈中某一個點(或者鏈底)覆蓋了。所以每種顏色在任何時刻,在樹上都是以一條連續的鏈存在。\(f(u, v)\) 不等於顏色段數,當且僅當有一種顏色多貢獻了顏色段數(即在 \(u\) 和 \(v\) 間出現的形式為至少兩段),而我們證明了一種顏色時刻都是連續的一條鏈,所以這是不可能的。
誒誒誒,這不就成 水原題 了嗎?甚至還是弱化版?你別急,你先別急,還有操作 \(3\) 呢。
操作 \(3\) 要求我們維護 \(f(1, u)\),並且能夠支援子樹查詢 \(\max\)。如果把樹拍到 dfs 序上,查詢簡單,考慮如何修改。
首先顏色段不好統計,根據經典小 trick,轉化為統計 \(1 \sim u\) 有多少 \(\operatorname{color}(v) \neq \operatorname{color}({\operatorname{fa}(v)})\),成了 \(v\) 上的一個點權。當然 \(1\) 的點權總是為 \(1\)。
有個暴力的想法,沿用之前樹剖 + 線段樹的方式,我們新開一棵樹。覆蓋 \(1 \sim u\),就在樹上二分出第一個 \(\operatorname{color}(v) \neq \operatorname{color}({\operatorname{fa}(v)})\) 的 \(v\),將原先和 \(\operatorname{fa}(v)\) 具有相同顏色的 \(v' \in \operatorname{son}(\operatorname{fa}(v))\) 的子樹做區間減一,把 \(v\) 的子樹做區間加一即可。邊界細節處理一下就可以了。樹上二分和區間加一隻 \(\log\) 沒什麼要說的,我們需要考慮會存在多少個 \(v\)。
事實上,我們猜測這樣的 \(v\) 不多,均攤下來,一個可能的上界是 \(\mathcal{O}(\log n)\)。
點選展開構造方式
考慮滿二叉樹,相當於一個 01-trie,每次往下走的時候,往和上一次訪問到這個結點不同的那個方向,這樣一定走滿 \(\Theta(\log n)\) 層來到葉子結點。
#include <cstdio>
#include <iostream>
#include <vector>
#include <random>
#include <algorithm>
#include <tuple>
#include <cassert>
using namespace std;
constexpr int n = 200000, m = 200000;
constexpr int p = __lg(n + 1);
constexpr int q = (1 << p) - 1;
constexpr int l = 1 << (p - 1);
vector<pair<int, int>> edges;
vector<tuple<int, int, int>> qrys, q1, q2;
mt19937 rnd(101108);
inline int rand(int l, int r) {
uniform_int_distribution<int> dist(l, r);
return dist(rnd);
}
int ls[n + 1], rs[n + 1];
bool lst[n + 1];
signed main() {
freopen("yzh", "w", stdout);
for (int u = 1; u <= q - l; ++u) {
ls[u] = u << 1, rs[u] = u << 1 | 1;
edges.emplace_back(u, ls[u]);
edges.emplace_back(u, rs[u]);
}
for (int u = q + 1; u <= n; ++u) {
int v;
do v = rand(q - l + 1, u - 1); while (ls[v] && rs[v]);
if (!ls[v] && !rs[v]) (rnd() & 1 ? ls : rs)[v] = u;
else if (!ls[v]) ls[v] = u;
else rs[v] = u;
edges.emplace_back(u, v);
}
shuffle(edges.begin(), edges.end(), rnd);
int leaf = 0;
for (int i = 1; i <= n; ++i) leaf += !ls[i] && !rs[i];
int t = max(1, m / leaf - rand(0, 2));
for (int _ = 1; _ <= t; ++_) {
for (int __ = 1; __ <= leaf; ++__) {
int u = 1;
while (ls[u] || rs[u]) {
if (!rs[u] || (lst[u] && ls[u]))
lst[u] = false, u = ls[u];
else
lst[u] = true, u = rs[u];
}
q1.emplace_back(1, u, 0);
}
}
for (int _ = t * leaf + 1; _ <= m; ++_) {
int op = rand(2, 3);
if (op == 2) {
q2.emplace_back(2, rand(1, n), rand(1, n));
} else {
q2.emplace_back(3, rand(1, n), 0);
}
}
auto it1 = q1.begin(), it2 = q2.begin();
for (int _ = 1; _ <= m; ++_) {
if (it1 != q1.end() && it2 != q2.end()) {
int $1 = q1.end() - it1, $2 = q2.end() - it2;
if (rand(1, $1 + $2) <= $1)
qrys.emplace_back(*it1++);
else
qrys.emplace_back(*it2++);
} else if (it2 == q2.end())
qrys.emplace_back(*it1++);
else
qrys.emplace_back(*it2++);
}
assert((int)edges.size() == n - 1);
assert((int)qrys.size() == m);
printf("%d %d %d\n", n, m, 0);
for (auto [u, v]: edges) {
if (rnd() & 1) swap(u, v);
printf("%d %d\n", u, v);
}
for (auto [op, u, v]: qrys) {
if (op == 2) printf("%d %d %d\n", op, u, v);
else printf("%d %d\n", op, u);
}
return 0;
}
由於我不會證明,所以這個時間複雜度可能是錯誤的,歡迎指出。
如此,總的時間複雜度為 \(\mathcal{O}(n + q \log^2 n)\)。
程式碼
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int MAX = 1 << 26;
char buf[MAX], *p = buf, obuf[MAX], *op = obuf;
#ifdef XuYueming
# define fread(_, __, ___, ____)
#else
# define getchar() *p++
#endif
#define putchar(x) *op++ = x
#define isdigit(x) ('0' <= x && x <= '9')
#define __yzh__(x) for (; x isdigit(ch); ch = getchar())
template <typename T>
inline void read(T &x) {
x = 0; char ch = getchar(); __yzh__(!);
__yzh__( ) x = (x << 3) + (x << 1) + (ch ^ 48);
}
template <typename T>
inline void write(T x) {
static short stack[10], top(0);
do stack[++top] = x % 10; while (x /= 10);
while (top) putchar(stack[top--] | 48);
putchar('\n');
}
const int N = 200010;
int n, m, ccnt;
int tp[N << 1], tl[N << 1];
vector<int> edge[N];
int son[N], siz[N], dpt[N];
int top[N], fa[N];
int L[N], R[N], timer;
int dfn[N];
void dfs(int now) {
siz[now] = 1;
for (int to: edge[now]) if (to != fa[now]) {
fa[to] = now, dpt[to] = dpt[now] + 1;
dfs(to), siz[now] += siz[to];
if (siz[to] > siz[son[now]]) son[now] = to;
}
}
void redfs(int now, int tp) {
dfn[L[now] = ++timer] = now;
top[now] = tp;
if (son[now]) redfs(son[now], tp);
for (int to: edge[now]) if (to != fa[now] && to != son[now]) redfs(to, to);
R[now] = timer;
}
namespace $1 {
struct Segment_Tree {
#define lson (idx << 1 )
#define rson (idx << 1 | 1)
struct Info {
int l, r, cnt;
friend Info operator + (const Info& a, const Info& b) {
return { a.l, b.r, a.cnt + b.cnt - (a.r == b.l) };
}
inline Info swap() const {
return { r, l, cnt };
}
};
struct node {
int l, r;
int tag;
Info info;
} tree[N << 2];
void build(int idx, int l, int r) {
tree[idx] = { l, r, -1, { 0, 0, 0 } };
if (l == r) {
++ccnt;
tree[idx].info = { ccnt, ccnt, 1 };
tp[ccnt] = tl[ccnt] = dfn[l];
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
tree[idx].info = tree[lson].info + tree[rson].info;
}
void pushtag(int idx, int tag) {
tree[idx].tag = tag;
tree[idx].info = {tag, tag, 1};
}
void pushdown(int idx) {
if (tree[idx].tag == -1) return;
pushtag(lson, tree[idx].tag);
pushtag(rson, tree[idx].tag);
tree[idx].tag = -1;
}
void modify(int idx, int l, int r, int tag) {
if (tree[idx].l > r || tree[idx].r < l) return;
if (l <= tree[idx].l && tree[idx].r <= r) return pushtag(idx, tag);
pushdown(idx);
modify(lson, l, r, tag);
modify(rson, l, r, tag);
tree[idx].info = tree[lson].info + tree[rson].info;
}
Info query(int idx, int l, int r) {
if (l <= tree[idx].l && tree[idx].r <= r) return tree[idx].info;
pushdown(idx);
if (r <= tree[lson].r) return query(lson, l, r);
if (l >= tree[rson].l) return query(rson, l, r);
return query(lson, l, r) + query(rson, l, r);
}
int query(int p) {
return query(1, p, p).l;
}
#undef lson
#undef rson
} yzh;
inline int lca(int u, int v) {
while (top[u] != top[v]) {
if (dpt[top[u]] < dpt[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dpt[u] < dpt[v] ? u : v;
}
inline int jump(int u, int p) {
int res = u;
while (top[u] != top[p])
res = top[u], u = fa[top[u]];
return u == p ? res : dfn[L[p] + 1];
}
inline void modify(int u, int v, int c) {
while (top[u] != top[v]) {
if (dpt[top[u]] < dpt[top[v]]) swap(u, v);
yzh.modify(1, L[top[u]], L[u], c);
u = fa[top[u]];
}
if (dpt[u] > dpt[v]) swap(u, v);
yzh.modify(1, L[u], L[v], c);
}
inline Segment_Tree::Info query(int u, int v, bool) {
Segment_Tree::Info res = {-1, -1, 0};
while (top[u] != top[v]) {
res = res + yzh.query(1, L[top[u]], L[u]).swap();
u = fa[top[u]];
}
res = res + yzh.query(1, L[v], L[u]).swap();
return res;
}
inline int query(int u, int v) {
int p = lca(u, v);
if (dpt[u] > dpt[v]) swap(u, v);
if (u == p) {
Segment_Tree::Info ans = query(v, u, true);
return ans.cnt;
} else {
int q = jump(u, p);
Segment_Tree::Info ans = query(u, q, true) + query(v, p, true).swap();
return ans.cnt;
}
}
}
namespace $2 {
struct Segment_Tree {
#define lson (idx << 1 )
#define rson (idx << 1 | 1)
struct node {
int l, r;
int tag;
int mx;
} tree[N << 2];
void pushup(int idx) {
tree[idx].mx = max(tree[lson].mx, tree[rson].mx);
}
void build(int idx, int l, int r) {
tree[idx] = { l, r, 0, 0 };
if (l == r) return tree[idx].mx = dpt[dfn[l]] + 1, void();
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(idx);
}
void pushtag(int idx, int tag) {
tree[idx].tag += tag;
tree[idx].mx += tag;
}
void pushdown(int idx) {
if (tree[idx].tag == 0) return;
pushtag(lson, tree[idx].tag);
pushtag(rson, tree[idx].tag);
tree[idx].tag = 0;
}
void modify(int idx, int l, int r, int tag) {
if (tree[idx].l > r || tree[idx].r < l) return;
if (l <= tree[idx].l && tree[idx].r <= r) return pushtag(idx, tag);
pushdown(idx);
modify(lson, l, r, tag);
modify(rson, l, r, tag);
pushup(idx);
}
int query(int idx, int l, int r) {
if (l > tree[idx].r || r < tree[idx].l) return 0;
if (l <= tree[idx].l && tree[idx].r <= r) return tree[idx].mx;
pushdown(idx);
return max(query(lson, l, r), query(rson, l, r));
}
#undef lson
#undef rson
} yzh;
void modify(int u, int c) {
tp[c] = 1, tl[c] = u;
for (int v; u; u = v) {
int cu = $1::yzh.query(L[u]);
v = fa[tp[cu]];
if (v) yzh.modify(1, L[tp[cu]], R[tp[cu]], -1);
if (u != tl[cu]) {
int p = $1::jump(tl[cu], u);
yzh.modify(1, L[p], R[p], 1);
tp[cu] = p;
}
}
}
inline int query(int u) {
return yzh.query(1, L[u], R[u]);
}
}
signed main() {
freopen("yzh", "r", stdin);
freopen("xym", "w", stdout);
fread(buf, 1, MAX, stdin);
read(n), read(m), read(*new int);
for (int i = 1, u, v; i < n; ++i) read(u), read(v), edge[u].push_back(v), edge[v].push_back(u);
dfs(1), redfs(1, 1), $1::yzh.build(1, 1, n), $2::yzh.build(1, 1, n);
for (int i = 1, op, u, v; i <= m; ++i) {
read(op), read(u);
if (op == 1) {
++ccnt;
$2::modify(u, ccnt);
$1::modify(1, u, ccnt);
} else if (op == 2) {
read(v);
write($1::query(u, v));
} else {
write($2::query(u));
}
}
fwrite(obuf, 1, op - obuf, stdout);
return 0;
}