P3995 树链剖分
适合树剖新手做的题,代码简单,思路稍微有点难理解,便于新手提升水平。
题目大意
给定一棵树,请你给出一种剖分方案(即输出所有结点的重儿子,剖分方案其实就是指对重儿子的判定规则),使所有询问操作总共访问的轻重链总条数最小,由于可能有许多合法方案,请任意输出一种。
解题思路
前置知识: 倍增求最近公共祖先,树上差分,对树剖的理解。(好像跟树剖没什么大关系)
预处理:
- dfs 预处理出深度和父亲。
- 倍增预处理。
回顾往事,常规树剖的重儿子都是取决于子树大小,所以 的定义是以 为根节点的子树大小。
分析样例,可以得出访问的轻重链总条数取决于点跳到链头的次数。
所以这道题就是让每一个节点的重儿子被访问的次数最多,即 应定义为一个节点的被访问次数。
注意这时的重儿子判断应该是:
if (siz[v] >= siz[hson[u]])
hson[u] = v;
因为原先的 最小为 ,所以我们只需用小于号就行了。但这题不一样, 有可能为 ,所以我们要用小于等于号才行。
详细一点,就是因为如果结点 只有一个儿子,然后这个儿子子树里没有询问,那 就一直为 了,但是 显然不是叶子节点。
对于一条询问路径,可以发现一个点的贡献会在 处消失,所以可以使用树上差分。
每读入一组询问,就把 和 加 ,而因为树链剖分的时候只会访问到 的下一层 ,我们就把 减 ,有一点类似边差分。
但是其实会有问题:
当 或 时,无论如何都要被操作一次,对我们来说不会有任何贡献,我们要让可以调整的查询尽量多的落在重儿子上。
那么剩下的就是直接用新的规则来进行树剖即可。
AC CODE
由于笔者快读代码太长,不适合放在这里,请自行加入快读。
#include <bits/stdc++.h>
#define _ 1000002
#define lson o << 1
#define rson o << 1 | 1
using namespace std;
int n, m;
int cnt;
array<int, _> head;
struct Edge
{
int to, nxt;
};
array<Edge, _ << 1> e;
array<int, _> siz, dep, hson;
array<array<int, 22>, _> fa; //等价于 int fa[_][22];
inline void add(int u, int v)
{
e[++cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
inline void dfs1(int u, int d = 1)
{
dep[u] = d;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (dep[v])
continue;
fa[v][0] = u;
dfs1(v, d + 1);
}
}
inline void dfs2(int u)
{
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == fa[u][0])
continue;
dfs2(v);
siz[u] += siz[v];
if (siz[v] > siz[hson[u]])
hson[u] = v;
}
}
int LCA(int u, int v)
{
if (dep[u] > dep[v])
swap(u, v);
for (int i = 20; i >= 0; --i)
if (dep[fa[v][i]] >= dep[u])
v = fa[v][i];
if (u == v)
return u;
for (int i = 20; i >= 0; --i)
if (fa[u][i] != fa[v][i])
{
u = fa[u][i];
v = fa[v][i];
}
return fa[u][0];
}
signed main()
{
cin >> n >> m;
for (int i = 1; i < n; ++i)
{
int a, b;
cin >> a >> b;
add(a, b);
add(b, a);
}
dfs1(1);
for (int j = 1; j <= 20; ++j)
for (int i = 1; i <= n; ++i)
fa[i][j] = fa[fa[i][j - 1]][j - 1];
for (int i = 1; i <= m; ++i)
{
int a, b;
cin >> a >> b;
int lca = LCA(a, b);
if (fa[a][0] != lca)
{
siz[a]++;
siz[lca]--;
}
if (fa[b][0] != lca)
{
siz[b]++;
siz[lca]--;
}
}
dfs2(1);
for (int i = 1; i <= n; ++i)
cout << hson[i] << endl;
return 0;
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122161