彩色圣诞树 题解
前言
笑点解析:特殊性质 \(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)\)。
这个结论未经证明,作者想了好久未能构造出比 \(\mathcal{O}(n \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;
}
代码
#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;
}
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/18445743。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。