HDU-5574 Colorful Tree 解题报告(原 2015-上海区域赛-C)
题意简述
¶一棵
0 u c
: 将以 为根节点的子树中所有的节点颜色染成1 u
: 输出以 为根节点的子树中颜色种数
数据范围:
题目简析
¶不能想到要用线段树进行查询,问题关键在于如何建树。如果直接用 DFS
序来表示树中的一段区间的话,维护颜色就举步维艰了,因为查询是一棵子树的颜色种数,普通的区间标记根本就无能为力。只好另谋出路。
注意到,在树中一个节点的颜色只会对其及祖先节点有贡献。具体地,一个节点的颜色更改可以视作将原来的颜色删除,再添加新的颜色。这样的话,删除颜色就对该节点及其祖先节点贡献 orange
对祖先节点的贡献):

上图中,orange
对节点 orange
、green
及 red
都有一个贡献值。但是,如果我们要把 violet
的颜色改成 orange
的话,则从 violet
-> red
这一条链中:
- 删除颜色 所有节点的贡献 -1
- 添加颜色 只对
violet
、blue
两个节点 +1。因为,节点red
、green
已被orange
这个颜色更新过了。
算法
¶不难想到,当修改一个节点 violet
颜色修改成 orange
这种情况对祖先节点的贡献):

如何找到
那么,
所以,我们只要对每一种颜色开一棵平衡树,键值为节点的 dfs 序。然后 lower_bound
, upper_bound
一下就可以找到
至于链上的操作树链剖分就可以了。
所以当我们执行一次 操作0 时,要把
同时,这样一来当前节点颜色并非总是对自己贡献
判断当前节点颜色是否在子孙节点中出现有一个小技巧:
123456int flag = 0;int c = getColor(u);if (c) { it = lower_bound(s[c].begin(), s[c].end(), st[u]); if (it == s[c].end() || *it > ed[u]) flag = 1;}
复杂度分析
¶由于删除一个节点的复杂度为
- 空间复杂度:
- 时间复杂度:
AC 代码:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249#include <bits/stdc++.h>using namespace std;
#define lc (o << 1)#define rc (o << 1 | 1)#define lson lc, lft, mid#define rson rc, mid + 1, rht#define MID(L, R) ((L) + (R) >> 1)
const int MAXN = 1e5 + 10;const int root = 1;
int N;
int from[MAXN], nxt[MAXN << 1], to[MAXN << 1], edge_siz;void addEdge(int u, int v) { to[++edge_siz] = u; nxt[edge_siz] = from[v]; from[v] = edge_siz;}
int father[MAXN], son[MAXN], dep[MAXN], siz[MAXN];void dfs(int o, int f, int d) { father[o] = f; son[o] = 0; dep[o] = d; siz[o] = 1;
for (int u = from[o]; u; u = nxt[u]) { int v = to[u]; if (v == f) continue;
dfs(v, o, d + 1); siz[o] += siz[v]; if (siz[son[o]] < siz[v]) son[o] = v; }}
int top[MAXN], st[MAXN], ed[MAXN], id[MAXN], dfs_clock;void dfs(int o, int t) { st[o] = ++dfs_clock; id[dfs_clock] = o; top[o] = t;
if (son[o]) { dfs(son[o], t); for (int u = from[o]; u; u = nxt[u]) { int v = to[u]; if (v != father[o] and v != son[o]) dfs(v, v); } }
ed[o] = dfs_clock;}
int lca(int u, int v) { while (top[u] != top[v]) { if (dep[top[u]] < dep[top[v]]) swap(u, v); u = father[top[u]]; } return dep[u] < dep[v] ? u : v;}
pair<int, int> T[MAXN << 2];int color[MAXN];
void pushdown(int o) { T[lc].first += T[o].first; T[rc].first += T[o].first; T[o].first = 0;}
void build(int o, int lft, int rht) { T[o].first = 0; if (lft == rht) { T[o].second = color[id[lft]]; } else { int mid = MID(lft, rht); build(lson); build(rson);
T[o].second = 1; }}
void update(int o, int lft, int rht, int L, int R, int V) { if (L <= lft and rht <= R) { T[o].first += V; } else { int mid = MID(lft, rht); if (L <= mid) update(lson, L, R, V); if (R > mid) update(rson, L, R, V); }}
int query(int o, int lft, int rht, int P) { if (lft == rht) return T[o].first; if (T[o].first) pushdown(o);
int mid = MID(lft, rht); if (P <= mid) return query(lson, P); return query(rson, P);}
void updatePath(int u, int v, int w) { while (top[u] != top[v]) { if (dep[top[u]] < dep[top[v]]) swap(u, v); update(1, 1, N, st[top[u]], st[u], w); u = father[top[u]]; } if (dep[u] > dep[v]) swap(u, v); update(1, 1, N, st[u], st[v], w);}
set<int> s[MAXN];set<int>::iterator it;
int lowerColor(int o, int lft, int rht, int L, int R) { if (!T[o].second) return 0; if (lft == rht) return T[o].second;
int mid = MID(lft, rht); int rcolor = 0; if (R > mid) rcolor = lowerColor(rson, L, R); return (!rcolor and L <= mid) ? lowerColor(lson, L, R) : rcolor;}
int getColor(int x) { while (top[x] != top[root]) { int ret = lowerColor(1, 1, N, st[top[x]], st[x]); if (ret) return ret; x = father[top[x]]; } return lowerColor(1, 1, N, st[root], st[x]);}
void maintain(int x, int c, int op) { if (~op) s[c].insert(st[x]); it = upper_bound(s[c].begin(), s[c].end(), st[x]); int pr = 0, su = 0;
if (it != s[c].end()) { su = lca(x, id[*it]); } --it; if (it != s[c].begin()) { --it; pr = lca(x, id[*it]); }
if (!pr and !su) updatePath(1, x, op); else { if (st[pr] < st[su]) pr = su;
updatePath(pr, x, op); updatePath(pr, pr, -op); }
if (op == -1) s[c].erase(st[x]);}
void remove(int o, int lft, int rht, int L, int R) { if (!T[o].second) return; if (lft == rht) { maintain(id[lft], T[o].second, -1); T[o].second = 0; } else { int mid = MID(lft, rht); if (L <= mid) remove(lson, L, R); if (R > mid) remove(rson, L, R);
T[o].second = T[lc].second or T[rc].second; }}
void insert(int o, int lft, int rht, int P, int C) { if (lft == rht) { maintain(id[lft], C, 1); T[o].second = C; } else { int mid = MID(lft, rht); if (P <= mid) insert(lson, P, C); else insert(rson, P, C);
T[o].second = 1; }}
void init() { memset(from, 0, sizeof from); edge_siz = 0;
for (int i = 1; i <= N; ++i) s[i].clear(); dfs_clock = 0;}
void work() { int T_T, Q, u, v, w, cmd;
scanf("%d", &T_T); for (int kase = 1; kase <= T_T; ++kase) { printf("Case #%d:\n", kase);
scanf("%d", &N); init(); for (int i = 1; i < N; ++i) { scanf("%d%d", &u, &v); addEdge(u, v); addEdge(v, u); } for (int i = 1; i <= N; ++i) scanf("%d", color + i);
dfs(root, -1, root); dfs(root, 1); build(1, 1, N);
for (int i = 1; i <= N; ++i) maintain(i, color[i], 1);
scanf("%d", &Q); while (Q--) { scanf("%d", &cmd); switch (cmd) { case 0: scanf("%d%d", &u, &v); remove(1, 1, N, st[u], ed[u]); insert(1, 1, N, st[u], v); break; case 1: scanf("%d", &u); int flag = 0; int c = getColor(u); if (c) { it = lower_bound(s[c].begin(), s[c].end(), st[u]); if (it == s[c].end() || *it > ed[u]) flag = 1; } printf("%d\n", query(1, 1, N, st[u]) + flag); break; } } }}
int main() { work(); return 0;}
小记
¶据说正解是
多谢小小兰学长的指教。
Related
¶