[bzoj3306]树——树上倍增+dfs序+线段树
2017-03-07 11:55
218 查看
Brief Description
您需要写一种数据结构,支持:
- 更改一个点的点权
- 求一个子树的最小点权
- 换根
Algorithm Design
我们先忽略第三个要求。
看到要求子树的最小点权,我们想到使用dfs序。容易看到,一个节点的子树在dfs序中的范围就是\([l(x),r(x)]\),所以我们把树结构变成了线性结构,从而变成了一个RMQ问题,我们使用线段树即可求解。
对于换根,我们不必重新求出拓扑结构。我们考察换根会影响到的节点。对于新根的子树中的节点,一定没有影响,对于不是新根祖先的节点,一定也没有影响。所以我们只考虑新根的祖先。
我们可以看出,换成新根以后,祖先的覆盖范围就变成了全树抛去新根到祖先路径上距离祖先距离最近的节点的子树大小,设这个点为y,那么dfs序中的范围就是\([1,l[y]-1] \cup [r[y]+1, n]\)。
所以问题就变成了如何求树上离某个点距离最近的儿子。显然可以使用树上倍增来求。
Code
#include <algorithm> #include <cstdio> using std::max; using std::min; const int maxn = 100010; int q[maxn], ind = 0, l[maxn], r[maxn], n, m, root, val[maxn], deep[maxn], fa[maxn][20]; int cnt = 0; struct edge { int to, next; } e[maxn]; int last[maxn]; struct seg { int l, r, mn; } t[maxn << 2]; void insert(int x, int y) { e[++cnt].to = y; e[cnt].next = last[x]; last[x] = cnt; } void update(int k) { t[k].mn = min(t[k << 1].mn, t[k << 1 | 1].mn); } void dfs(int x) { l[x] = ++ind; q[ind] = x; for (int i = 1; i <= 16; i++) { if (deep[x] < (1 << i)) break; fa[x][i] = fa[fa[x][i - 1]][i - 1]; } for (int i = last[x]; i; i = e[i].next) { fa[e[i].to][0] = x; deep[e[i].to] = deep[x] + 1; dfs(e[i].to); } r[x] = ind; } void build(int k, int l, int r) { t[k].l = l, t[k].r = r; int mid = (l + r) >> 1; if (l == r) { t[k].mn = val[q[l]]; return; } build(k << 1, l, mid); build(k << 1 | 1, mid + 1, r); t[k].mn = min(t[k << 1].mn, t[k << 1 | 1].mn); } void modify(int k, int pos, int val) { int l = t[k].l, r = t[k].r, mid = (l + r) >> 1; if (l == r) { t[k].mn = val; return; } if (pos <= mid) modify(k << 1, pos, val); else modify(k << 1 | 1, pos, val); update(k); } int query(int k, int x, int y) { int l = t[k].l, r = t[k].r, mid = (l + r) >> 1; if (x <= l && r <= y) return t[k].mn; int ans = 0x3f3f3f; if (x <= mid) ans = min(ans, query(k << 1, x, y)); if (y > mid) ans = min(ans, query(k << 1 | 1, x, y)); return ans; } int main() { #ifndef ONLINE_JUDGE freopen("input", "r", stdin); #endif scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) { int f; scanf("%d %d", &f, &val[i]); if (f) insert(f, i); } dfs(root = 1); #ifndef ONLINE_JUDGE for (int i = 1; i <= ind; i++) printf("%d ", q[i]); printf("\n"); #endif build(1, 1, n); while (m--) { char ch[5]; int x; scanf("%s %d", ch, &x); if (ch[0] == 'V') { int val; scanf("%d", &val); modify(1, l[x], val); } else if (ch[0] == 'E') root = x; else { if (root == x) printf("%d\n", t[1].mn); else if (l[x] <= l[root] && r[x] >= r[root]) { // x is the father of root int y = root, d = deep[y] - deep[x] - 1; for (int i = 0; i <= 16; i++) if (d & (1 << i)) y = fa[y][i]; printf("%d\n", min(query(1, 1, l[y] - 1), query(1, r[y] + 1, n))); } else printf("%d\n", query(1, l[x], r[x])); } } return 0; }
相关文章推荐
- cf588e & bzoj3306 树上倍增
- [HNOI2014][BZOJ3572] 世界树|虚树|树上倍增LCA|树型dp|dfs序
- 【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树
- 【NOIP2016练习&BZOJ2125】T3 sp (树上倍增,最短路)
- bzoj1787 [Ahoi2008]Meet 紧急集合 树上倍增
- BZOJ 4082 Wf2014 Surveillance 树上倍增
- [JLOI2014][BZOJ3631] 松鼠的新家|树上倍增LCA|差分
- bzoj4242 水壶 最小生成树&树上倍增
- 【bzoj3779】重组病毒 LCT+树上倍增+DFS序+树状数组区间修改区间查询
- 【bzoj4281】[ONTAK2015]Związek Harcerstwa Bajtockiego 树上倍增+LCA
- 3306: 树 树上倍增
- [SDOI2015][BZOJ3991] 寻宝游戏|set|dfs序|虚树|树上倍增LCA
- 【bzoj2783】[JLOI2012]树 树上倍增
- 【bzoj1977】【次小生成树】【树上倍增】
- 【bzoj3488】[ONTAK2010]Highways DFS序+树上倍增+树状数组
- bzoj1977: [BeiJing2010组队]次小生成树 Tree 树上倍增
- BZOJ 4281 [ONTAK2015]Związek Harcerstwa Bajtockiego 树上倍增
- [树上倍增] BZOJ 4281 [ONTAK2015]Związek Harcerstwa Bajtockiego
- [SDOI2011][BZOJ2286] 消耗战|虚树|树型dp|树上倍增LCA
- [HEOI2014][BZOJ3611] 大工程|虚树|树型dp|dfs序|树上倍增LCA