CF2161F SubMST 题解
Description
给定一棵无向无权树 \(T\),它有 \(n\) 个顶点。
定义一个包含 \(n\) 个顶点的完全无向加权图 \(G\),其中 \(G\) 中顶点 \(u\) 和 \(v\) 之间的边权等于它们在原树 \(T\) 中的最短距离。
对于所有可能的顶点子集,考虑由该子集诱导出的 \(G\) 的子图,并求出该子图的最小生成树的权值。
要求计算所有可能顶点子集的最小生成树权值之和。
由于这个数可能非常大,你只需要输出结果对 \(10^9 + 7\) 取模后的值。
\(n\leq 5000\)。
Solution
首先最小生成树显然是无法直接做的,考虑用类似 kruskal 算法的方式,按照边权从小到大加边,计算每种边权的边总共加了多少条。
对于所有距离不超过 \(k\) 的点之间的连边可以看成钦定一个“中点”\(x\)(可以是边上的中点),然后将所有与 \(x\) 距离不超过 \(\frac{k}{2}\) 的点两两连边。
计算求最小生成树上边权为 \(k\) 的边加入的数量时,把这些距离为 \(k\) 的边放在中点上计算。
考虑一组选点方案的最小生成树中,挂在中点 \(x\) 处有长度为 \(k\) 的边的条件是什么。
经过手玩,可以发现条件是 \(x\) 所有子树中距离 \(x\) 的最短距离不小于 \(\frac{k}{2}\),且存在至少两个子树最短距离恰好是 \(\frac{k}{2}\)。
证明
由于存在挂在 \(x\) 上的距离为 \(k\) 的边,所以肯定要有至少两个子树最短距离是 \(\frac{k}{2}\)。如果存在一个子树最短距离小于 \(\frac{k}{2}\),就说明那两个最短距离是 \(\frac{k}{2}\) 的子树可以与这个最近的子树连距离小于 \(k\) 的边,也就矛盾了。
挂在 \(x\) 的边数也就是最短距离是 \(\frac{k}{2}\) 的子树数量减一。
容易发现这么计算不会算少,但是会不会算多呢?即两个通过距离小于 \(k\) 构成的连通块之间会不会有很多距离等于 \(k\) 的边,且中点不唯一,此时就会算重。经过手玩,会发现这是不必要的关心。
证明
如果不唯一,则设这对连通块之间的一条长度为 \(k\) 的边是 \((a,b)\),中点是 \(x\);另一条边是 \((c,d)\),中点是 \(y\)(\(x\neq y\))。
那么 \(a,c\) 与 \(b,d\) 分别在 \(x\) 的同一个子树中。且 \(\text{dis}(x,a)=\text{dis}(x,b)=\frac{k}{2}\),\(\text{dis}(x,c)+\text{dis}(x,d)=k\),\(\text{dis}(x,c),\text{dis}(x,d)\geq\frac{k}{2}\)。
由于 \(x\neq y\),所以 \(\text{dis}(x,c)\neq\text{dis}(x,d)\),这说明 \(\text{dis}(x,c)\) 和 \(\text{dis}(x,d)\) 至少有一个数小于 \(\frac{k}{2}\),与 \(x\) 每个子树的最短距离都大于等于 \(\frac{k}{2}\) 矛盾。
所以根据上面的做法,枚举中点 \(x\) 和距离 \(k\) 后,对于 \(x\) 的每个儿子的子树,对它内部最短距离等于 \(\frac{k}{2}\) 的方案数 乘上其余子树最短距离都大于等于 \(\frac{k}{2}\) 的方案数求和。
最后减去所有子树的最短距离都大于等于 \(\frac{k}{2}\),且至少一个子树的最短距离是 \(\frac{k}{2}\) 的方案数即可。
时间复杂度:\(O(n^2)\)。
Code
#include <bits/stdc++.h>
// #define int int64_t
const int kMaxN = 1e4 + 5, kMod = 1e9 + 7;
int n;
int pw[kMaxN], dep[kMaxN];
int dfn_cnt, dfn[kMaxN], idx[kMaxN], sz[kMaxN];
int cnt[kMaxN];
std::vector<int> G[kMaxN];
int qpow(int bs, int64_t idx = kMod - 2) {
int ret = 1;
for (; idx; idx >>= 1, bs = (int64_t)bs * bs % kMod)
if (idx & 1)
ret = (int64_t)ret * bs % kMod;
return ret;
}
inline int add(int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); }
inline int sub(int x, int y) { return (x >= y ? x - y : x - y + kMod); }
inline void inc(int &x, int y) { (x += y) >= kMod ? x -= kMod : x; }
inline void dec(int &x, int y) { (x -= y) < 0 ? x += kMod : x; }
void prework(int n = 1e4) {
pw[0] = 1;
for (int i = 1; i <= n; ++i) pw[i] = 2ll * pw[i - 1] % kMod;
}
void dfs(int u, int fa) {
idx[dfn[u] = ++dfn_cnt] = u, sz[u] = 1, dep[u] = dep[fa] + 1;
for (auto v : G[u]) {
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
}
}
void solve(int u) {
dfn_cnt = 0, dep[0] = -1, dfs(u, 0);
static int cc[kMaxN];
std::fill_n(cc + 1, 2 * n + 1, 0);
for (auto v : G[u]) {
for (int i = dfn[v]; i <= dfn[v] + sz[v] - 1; ++i) {
int x = idx[i];
if (x <= n) ++cc[dep[x]];
}
}
for (int i = 2 * n; i; --i) {
cc[i] += cc[i + 1];
dec(cnt[i], sub(pw[cc[i]], pw[cc[i + 1]]));
}
for (auto v : G[u]) {
static int ct[kMaxN];
std::fill_n(ct + 1, 2 * n + 1, 0);
for (int i = dfn[v]; i <= dfn[v] + sz[v] - 1; ++i) {
int x = idx[i];
if (x <= n) ++ct[dep[x]];
}
for (int i = 2 * n ; i; --i) {
ct[i] += ct[i + 1];
inc(cnt[i], 1ll * sub(pw[ct[i]], pw[ct[i + 1]]) * pw[cc[i] - ct[i]] % kMod);
}
}
// for (int i = 1; i <= n; ++i) dec(cnt[i], sub(pw[cc[i]], pw[cc[i + 1]]));
}
void dickdreamer() {
std::cin >> n;
for (int i = 0; i <= 2 * n - 1; ++i) G[i].clear(), cnt[i] = 0;
for (int i = 1; i < n; ++i) {
int u, v;
std::cin >> u >> v;
G[u].emplace_back(i + n), G[i + n].emplace_back(u);
G[v].emplace_back(i + n), G[i + n].emplace_back(v);
}
for (int i = 1; i <= 2 * n - 1; ++i) solve(i);
int ans = 0;
for (int i = 1; i <= n; ++i) inc(ans, 1ll * i * cnt[i] % kMod);
std::cout << ans << '\n';
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
std::cin >> T;
prework();
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

浙公网安备 33010602011771号