安吉D11-A
- 1安吉D19 T1
- 2安吉D19 T3
- 3安吉D20 模考12总结
- 4安吉D17 T3
- 5安吉D17 T4
- 6安吉D19 模考11总结
- 7安吉D15 A
- 8安吉D16 T4
- 9安吉D17 T1
- 10安吉D17 模考10总结
- 11安吉D16 T3
- 12安吉D16 模考9总结
- 13安吉D13 模考8总结
- 14安吉D13-T2
- 15安吉D13-T3
- 16安吉D12-T3
- 17安吉D11-B
- 18安吉D11-C
- 19安吉D11-D
- 20安吉D12 模考7总结
- 21安吉D12-T2
- 22安吉D11-A本文
- 23安吉D10 模考6总结
- 24安吉D10-T3
- 25安吉D10-T4
- 26安吉D8-E
- 27安吉D9-T4
- 28安吉D8-A
- 29安吉D8-B
- 30安吉D8-C
- 31安吉D8-D
- 32安吉D9 模考5总结
- 33安吉D9-T2
- 34安吉D9-T3
- 35安吉D6-T4
- 36安吉D4-G
- 37安吉D5-T2
- 38安吉D6 模考4总结
- 39安吉D6-T1
- 40安吉D6-T3
- 41安吉-开营测试 T4
- 42安吉D5 模考3总结
- 43安吉D5-T3
- 44安吉D4-A
- 45安吉D4-B
- 46安吉D4-C
- 47安吉D4:容斥原理
- 48安吉Day4-D
- 49安吉D3 模考2总结
- 50安吉D3-T1
- 51安吉D3-T2
- 52安吉D3-T4
- 53安吉D2 模考1总结
- 54安吉D2-T2
- 55安吉D1-G
- 56安吉D1-L
- 57安吉D2-T4
原题呈现
P6374 树上询问
题目描述
给定一棵 个点的无根树,有 次询问.
每次询问给一个参数三元组 ,求有多少个 满足这棵树在以 为根的情况下 和 的最近公共祖先为 .
输入格式
第一行 个数,为 和 .
接下来 行,每行 个数,表示树的一条边.
接下来 行,每行 个数,为 .
输出格式
共 行,每行一个数,为对于每个三元组的 的个数.
输入输出样例 #1
输入 #1
10 51 21 32 42 52 105 63 77 87 94 6 24 10 16 8 39 10 24 10 5输出 #1
70140输入输出样例 #2
输入 #2
5 31 31 53 43 25 2 35 2 12 4 5输出 #2
210输入输出样例 #3
输入 #3
20 101 21 31 42 52 63 104 134 146 76 810 114 154 168 911 1216 1716 1816 1917 2015 19 161 12 120 20 207 7 81 8 35 20 22 9 69 12 19 12 29 12 3输出 #3
4162000521021说明/提示
样例 2 解释

第一个查询的 为 和 .
第二个查询的 为 .
数据范围
本题按子任务测试:
-
Subtask 1( pts):,.
-
Subtask 2( pts):,,树退化成链.
-
Subtask 3( pts):,,数据不随机.
-
Subtask 4( pts): ,.
对于所有数据:,.
注:数据强度不高,不必卡常与快读快输.
考虑样例 2,当询问为 时,此时,
| 根节点 | lca(2,5) |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 3 |
| 5 | 5 |
显然,不在 这条链上的 不可能为两节点的最近公共祖先.实际上,对于询问 ,不在 这条链上的所有点 ,若满足 ,则方案数必然为 .因为若 与 连通时存在一个汇入点 ,满足 和 交于点 ,且点 在 上,则此时路径 和 会有重合().因此, 不能成为 的最近公共祖先.
那对于 在 上的情况怎么办呢?那么此时首先, 自身可以作为根节点.其次,不在 上的点,且与 连通的点可以将 拎起来.因此,答案就是不在 上且与 直接相连(这里指不通过 上的任何边就能够相连)的点以及 本身的节点数量.
考虑如何 算出.显然,我们可以以 为根建树进行一次 dfs,那么,对于询问 ,会有四种可能:
- 不在 上.此时根据分析,输出 0.
- 正好是 的最近公共祖先.此时,所有可能的根节点就是 去除含有 的两颗子树(这里含有……的子树指所有以 子节点为根节点的子树中含有该节点的子树,下同)大小,剩余的图中所有点都可以作根
- 在 的最近公共祖先的含有 的子树上,如上述样例中的 , 就在 最近公共祖先 的含有 的子树上.再如下图中的 ,那么此时, 的子树中含有 的子树部分(即图中节点 )肯定不行(因为子树上的任意一点都需要经过 ,这条边在 上),且此时除了 子树之外的部分 (即图中点 )也不能作为子节点(因为 上的每一个点想要到达 都需要经过 ,显然 不在 的子树上,故 也需要通过 ,因此 在 上)因此答案为:所有的节点,减去 子树中含有 的子树大小,再减去不在 子树中的节点.
- 在 的最近公共祖先的有 的子树上,这一种和上一种相同.
这需要用到 dfs 序,所谓 dfs 序,就是通过 dfs 首次先序遍历到每一个节点的次序,这可以在 dfs 的时候预处理出来.节点 的 dfs 序常记为 . 不难发现,同一个子树内的节点, 是连续的.可以利用这个性质,若我们现在想要知道 是否是 的子节点,那么这个命题完全等同于:
,其中, 为 及其子节点的数量.
可以写出代码:
#include <bits/stdc++.h>using namespace std;const int N = 5e5 + 100;int n, q;vector<int> e[N];int f[22][N], cnt, dfn[N], sz[N], dep[N];
int LCA(int x, int y) { if (dep[x] > dep[y]) { swap(x, y); } int step = dep[y] - dep[x]; for (int i = 20; i >= 0; i--) { if (step & (1 << i)) { y = f[i][y]; } } if (x == y) return x; for (int i = 20; i >= 0; i--) { if (f[i][x] != f[i][y]) { x = f[i][x]; y = f[i][y]; } } return f[0][x];}
void dfs(int t, int fa) { f[0][t] = fa; dep[t] = dep[f[0][t]] + 1; dfn[t] = ++cnt; for (int i = 0; i < e[t].size(); i++) { if (e[t][i] != fa) dfs(e[t][i], t); } for (int i = 0; i < e[t].size(); i++) { if (e[t][i] != fa) sz[t] += sz[e[t][i]]; } sz[t]++;}
/** * 判断 x 是不是在 y 的子树中 */bool isIn(int x, int y) { return dfn[y] <= dfn[x] && dfn[x] < dfn[y] + sz[y]; }
/** * 通过倍增计算以fa子节点为根节点的子树中含有dir的子树 */int getSubSz(int dir, int fa) { if (dir == fa) { return 0; } for (int i = 20; i >= 0; i--) { int now = f[i][dir]; if (dfn[now] > dfn[fa]) { dir = now; } } return sz[dir];}
signed main() { cin >> n >> q; for (int i = 1; i <= n - 1; i++) { int u, v; cin >> u >> v; e[u].push_back(v); e[v].push_back(u); } dfs(1, 0); for (int j = 1; j <= 20; j++) { for (int i = 1; i <= n; i++) { f[j][i] = f[j - 1][f[j - 1][i]]; } } for (int i = 1; i <= q; i++) { int a, b, c; cin >> a >> b >> c; if (LCA(a, b) == c) { cout << n - getSubSz(a, c) - getSubSz(b, c) << endl; } else if (isIn(a, c) && !isIn(b, c)) { cout << n - getSubSz(a, c) - (n - sz[c]) << endl; } else if (!isIn(a, c) && isIn(b, c)) { cout << n - getSubSz(b, c) - (n - sz[c]) << endl; } else { cout << 0 << endl; } } return 0;}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


