CF917D. Stranger Trees & TopCoder13369. TreeDistance(变元矩阵树定理+高斯消元)
题目链接
CF917D:https://codeforces.com/problemset/problem/917/D
TopCoder13369:https://community.topcoder.com/stat?c=problem_statement&pm=13369
题解
首先分析 CF917D。
我们考虑能否将树上的边的贡献特殊表现出来。
记原树为 ,我们构造一幅 个结点的无向完全图,并设置一个值 ,对于无向边 ,其权值 满足:
如果我们令无向完全图的所有生成树中,恰好有 条边属于原树 的生成树数量为 ,那么不难发现,根据变元矩阵树定理(基尔霍夫矩阵的任意 阶主子式的行列式的绝对值即为所有生成树的边权积之和),求得的基尔霍夫矩阵的 阶主子式的值即为 ,因为若一个生成树包含了 条原树边,那么该生成树的边权积即为 。
如果我们将 视为未知数,那么我们就可以通过枚举 来得到一个 元线性方程组。直接高斯消元即可求得所有的 。
接下来分析 TopCoder13369。
考虑原树 最少能通过多少次操作得到另一棵树 ,显然若在原树 中出现而在树 中没有出现的边数为 ,那么只需要 次操作即可将原树 变为树 。因此答案即为有 条边属于原树的生成树数量,即 。其余部分与上题相同。
两题的时间复杂度均为 。
代码
CF917D 代码如下:
#include<bits/stdc++.h> using namespace std; const int N = 105, mod = 1e9 + 7; void add(int& x, int y) { x += y; if (x >= mod) { x -= mod; } } void sub(int& x, int y) { x -= y; if (x < 0) { x += mod; } } int mul(int x, int y) { return (long long) x * y % mod; } int qpow(int v, int p) { int result = 1; for (; p; p >>= 1, v = mul(v, v)) { if (p & 1) { result = mul(result, v); } } return result; } int n, a[N][N], b[N][N]; bool old[N][N]; int matrix_tree() { int result = 1; for (int i = 2; i <= n; ++i) { int rev = i; for (int j = i + 1; j <= n; ++j) { if (a[j][i]) { rev = j; break; } } if (rev != i) { result = (mod - result) % mod; for (int j = i; j <= n; ++j) { swap(a[i][j], a[rev][j]); } } for (int j = i + 1; j <= n; ++j) { int p = mul(a[j][i], qpow(a[i][i], mod - 2)); for (int k = i; k <= n; ++k) { sub(a[j][k], mul(p, a[i][k])); } } result = mul(result, a[i][i]); } return result; } int main() { scanf("%d", &n); for (int i = 1; i < n; ++i) { int u, v; scanf("%d%d", &u, &v); old[u][v] = old[v][u] = true; } for (int x = 0; x < n; ++x) { memset(a, 0, sizeof a); for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { int v = old[i][j] ? x : 1; add(a[j][j], v); sub(a[i][j], v); } } for (int i = 0, v = 1; i < n; ++i, v = mul(v, x)) { b[x][i] = v; } b[x][n] = matrix_tree(); } for (int i = 0; i < n; ++i) { int rev = i; for (int j = i + 1; j < n; ++j) { if (b[j][i]) { rev = j; break; } } if (rev != i) { for (int j = i; j <= n; ++j) { swap(b[i][j], b[rev][j]); } } for (int j = i + 1; j < n; ++j) { int p = mul(b[j][i], qpow(b[i][i], mod - 2)); for (int k = i; k <= n; ++k) { sub(b[j][k], mul(p, b[i][k])); } } } for (int i = n - 1; ~i; --i) { for (int j = i + 1; j < n; ++j) { sub(b[i][n], mul(b[i][j], b[j][n])); } b[i][n] = mul(b[i][n], qpow(b[i][i], mod - 2)); } for (int i = 0; i < n; ++i) { printf("%d%c", b[i][n], " \n"[i == n - 1]); } return 0; }
TopCoder13369 代码如下:
直接粘过来改一下即可。
#include<bits/stdc++.h> using namespace std; const int N = 55, mod = 1e9 + 7; void add(int& x, int y) { x += y; if (x >= mod) { x -= mod; } } void sub(int& x, int y) { x -= y; if (x < 0) { x += mod; } } int mul(int x, int y) { return (long long) x * y % mod; } int qpow(int v, int p) { int result = 1; for (; p; p >>= 1, v = mul(v, v)) { if (p & 1) { result = mul(result, v); } } return result; } int n, a[N][N], b[N][N]; bool old[N][N]; int matrix_tree() { int result = 1; for (int i = 1; i < n; ++i) { int rev = i; for (int j = i + 1; j < n; ++j) { if (a[j][i]) { rev = j; break; } } if (rev != i) { result = (mod - result) % mod; for (int j = i; j < n; ++j) { swap(a[i][j], a[rev][j]); } } for (int j = i + 1; j < n; ++j) { int p = mul(a[j][i], qpow(a[i][i], mod - 2)); for (int k = i; k < n; ++k) { sub(a[j][k], mul(p, a[i][k])); } } result = mul(result, a[i][i]); } return result; } class TreeDistance { public: int countTrees(vector<int> p, int c) { n = p.size() + 1; for (int i = 0; i < p.size(); ++i) { old[p[i]][i + 1] = old[i + 1][p[i]] = true; } for (int x = 0; x < n; ++x) { memset(a, 0, sizeof a); for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { int v = old[i][j] ? x : 1; add(a[j][j], v); sub(a[i][j], v); } } for (int i = 0, v = 1; i < n; ++i, v = mul(v, x)) { b[x][i] = v; } b[x][n] = matrix_tree(); } for (int i = 0; i < n; ++i) { int rev = i; for (int j = i + 1; j < n; ++j) { if (b[j][i]) { rev = j; break; } } if (rev != i) { for (int j = i; j <= n; ++j) { swap(b[i][j], b[rev][j]); } } for (int j = i + 1; j < n; ++j) { int p = mul(b[j][i], qpow(b[i][i], mod - 2)); for (int k = i; k <= n; ++k) { sub(b[j][k], mul(p, b[i][k])); } } } for (int i = n - 1; ~i; --i) { for (int j = i + 1; j < n; ++j) { sub(b[i][n], mul(b[i][j], b[j][n])); } b[i][n] = mul(b[i][n], qpow(b[i][i], mod - 2)); } int answer = 0; for (int i = n - 1; i >= max(0, n - 1 - c); --i) { add(answer, b[i][n]); } return answer; } }; /* TreeDistance solver; int main() { int n, k; scanf("%d", &n); vector<int> p(n - 1); for (int i = 0; i < n - 1; ++i) { scanf("%d", &p[i]); } scanf("%d", &k); printf("%d\n", solver.countTrees(p, k)); return 0; } */
标签:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?