专栏文章
P11624 挂掉的模版 题解
P11624题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miqdk5nk
- 此快照首次捕获于
- 2025/12/04 03:03 3 个月前
- 此快照最后确认于
- 2025/12/04 03:03 3 个月前
P11624 挂掉的模版 题解
记号约定
- 、:如原题所述。
- :u 与 v 的最近公共祖先
- :根节点
- :节点 u 的深度
- :以 u 为根的子树的大小(节点数)
- :在统计范围内深度等于 d 的节点数
小观察
以下讨论 的情况。显然,u 和 v 的跳转次数相同。
一个节点到达根节点后,会停留在根节点。
图为样例 3,以 为例,图中圆框为 u 跳转得,方框为 v 跳转得,同种颜色为跳转同样次数。可见,当 u 和 v 深度不同时,只有根节点处跳转次数相同,所以 。
于是,我们可以得出结论, 当且仅当满足以下两种情况:
- (a)
其中第二种情况可以分为:
- u 与 v 分属根节点不同子节点的子树(b)
- 或 (c)
解法
答案即为满足(a)(b)(c)中至少一条的有序节点对数。
但是注意到,(a)与(b)可能重叠。因此,采用以下策略:
但是注意到,(a)与(b)可能重叠。因此,采用以下策略:
- 满足(c)的总共 对,即 ,有 和 两对,并减去重复计算的 。
- DFS 一遍,统计出满足(b)的对数。注意到,有序对数即为无序对数的 2 倍。(本条中“答案”指有序对数)
对于一个要并入答案的子树 ,增加了: 注意到, 可以在计算答案后维护,则计算上式的值为 的。 - 统计出满足(a)而不满足(b)的无序对数,即在以根节点的各个子节点为根的子树内统计出 ,并将答案增加 。
时间复杂度
除根节点外,每个节点都被 DFS 了两遍,而节点深度不会超过 ,故为 。
Code
CPP#include <bits/stdc++.h>
#define UP(i, l, r) for(register int (i) = (l); (i) <= (r); ++ (i))
#define DN(i, l, r) for(register int (i) = (r); (i) >= (l); -- (i))
#define EUP(t, i, l, r, d) for(register (t) (i) = (l); (i) <= (r); (i) += (d))
#define EDN(t, i, l, r, d) for(register (t) (i) = (r); (i) >= (l); (i) -= (d))
#define INF 0x3f3f3f3f
#define INFLL 0x3f3f3f3f3f3f3f3f
#define INFB 0x3f
using namespace std;
using ll = long long;
const int N = 1e6;
int n, fa[N + 5], sz[N + 5], md, rt;
vector<int> g[N + 5];
ll ans, l[N + 5], ss;
void dfs1(int u){
sz[u] = 1;
for(register int v : g[u]){
dfs1(v);
sz[u] += sz[v];
}
if(fa[u] == rt && u != rt){
ans += ss * sz[u];
ss += sz[u];
}
}
void dfs2(int u, int d){
md = max(md, d);
++ l[d];
for(register int v : g[u]) dfs2(v, d + 1);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
if(n == 1){
cout << 1;
return 0;
}
UP(i, 1, n){
cin >> fa[i];
if(fa[i] == i) rt = i;
else g[fa[i]].push_back(i);
}
ans += (n << 1) - 1; // c
dfs1(rt); // b
ans <<= 1; // 无序转有序
for(register int r : g[rt]){ // a & !b
UP(i, 1, md) l[i] = 0;
md = 0;
dfs2(r, 1);
UP(i, 1, md) ans += l[i] * l[i];
}
cout << ans;
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...