东方14ACM小组水题0027 会议召开 树

mac2022-07-05  14

题目链接

http://west14.openjudge.cn/verywater/0027/

分析

补集转化,关键在于求出有多少场会议不需要额外场地;

假设已确定两场会议的位置,若第三场会议的位置在这两点的路径上,是符合要求的;

而在两端确定的情况下,这样的点只有两端间路径长度 − 1 -1 1 个;

统计出所有路径的长度再减去路径数量即为答案;

每条边会对所有路径长度之和贡献 s i z e [ v ] ∗ ( n − s i z e [ v ] ) size[v] * (n - size[v]) size[v](nsize[v])

s i z e [ v ] size[v] size[v] 是以该边深度较大点为根的子树大小;

路径数量则可以在DFS某点时,用新子树数量乘已有子树数量(包括该点)累加得到。

AC代码

#include <cstdio> inline int read() { int num = 0; char c = getchar(); while (c < '0' || c > '9') c = getchar(); while (c >= '0' && c <= '9') num = num * 10 + c - '0', c = getchar(); return num; } const int maxn = 1e5 + 5; int head[maxn], eid; struct Edge { int v, next; } edge[2 * maxn]; inline void insert(int u, int v) { edge[++eid].v = v; edge[eid].next = head[u]; head[u] = eid; } int n, size[maxn]; long long ans = 0; void dfs(int u, int fa) { size[u] = 1; for (int p = head[u]; p; p = edge[p].next) { int v = edge[p].v; if (v == fa) continue; dfs(v, u); ans -= 1ll * size[v] * (n - size[v]) - 1ll * size[u] * size[v]; size[u] += size[v]; } } int main() { n = read(); for (int i = 1; i < n; ++i) { int u = read(), v = read(); insert(u, v), insert(v, u); } ans = 1ll * n * (n - 1) * (n - 2) / 6; dfs(0, -1); printf("%lld", ans); return 0; }
最新回复(0)