题目链接

CF917D:https://codeforces.com/problemset/problem/917/D

TopCoder13369:https://community.topcoder.com/stat?c=problem_statement&pm=13369

题解

首先分析 CF917D。

我们考虑能否将树上的边的贡献特殊表现出来。

记原树为 \(T\),我们构造一幅 \(n\) 个结点的无向完全图,并设置一个值 \(x\),对于无向边 \((u, v)\),其权值 \(w_{(u, v)}\) 满足:

\[w_{(u, v)} = \begin{cases} x, & (u, v) \in T \\ 1, & (u, v) \notin T \end{cases}
\]

如果我们令无向完全图的所有生成树中,恰好有 \(i\) 条边属于原树 \(T\) 的生成树数量为 \({\rm ans}_i\),那么不难发现,根据变元矩阵树定理(基尔霍夫矩阵的任意 \(n - 1\) 阶主子式的行列式的绝对值即为所有生成树的边权积之和),求得的基尔霍夫矩阵的 \(n - 1\) 阶主子式的值即为 \(\sum_\limits{i = 0}^{n - 1} x^i {\rm ans}_i\),因为若一个生成树包含了 \(i\) 条原树边,那么该生成树的边权积即为 \(x^i\)。

如果我们将 \({\rm ans}_i\) 视为未知数,那么我们就可以通过枚举 \(x: 0 \sim n - 1\) 来得到一个 \(n\) 元线性方程组。直接高斯消元即可求得所有的 \({\rm ans}_i\)。

接下来分析 TopCoder13369。

考虑原树 \(T\) 最少能通过多少次操作得到另一棵树 \(T'\),显然若在原树 \(T\) 中出现而在树 \(T'\) 中没有出现的边数为 \(w\),那么只需要 \(w\) 次操作即可将原树 \(T\) 变为树 \(T'\)。因此答案即为有 \(i(i \geq n - 1 - k)\) 条边属于原树的生成树数量,即 \(\sum_\limits{i = n - 1 - k}^{n - 1} {\rm ans}_i\)。其余部分与上题相同。

两题的时间复杂度均为 \(O(n^4)\)。

代码

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;
}
*/

CF917D. Stranger Trees & TopCoder13369. TreeDistance(变元矩阵树定理+高斯消元)的更多相关文章

  1. P3317 [SDOI2014]重建 变元矩阵树定理 高斯消元

    传送门:https://www.luogu.org/problemnew/show/P3317 这道题的推导公式还是比较好理解的,但是由于这个矩阵是小数的,要注意高斯消元方法的使用: #include ...

  2. 【BZOJ3534】【Luogu P3317】 [SDOI2014]重建 变元矩阵树,高斯消元

    题解看这里,主要想说一下以前没见过的变元矩阵树还有前几个题见到的几个小细节. 邻接矩阵是可以带权值的.求所有生成树边权和的时候我们有一个基尔霍夫矩阵,是度数矩阵减去邻接矩阵.而所谓变元矩阵树实际上就是 ...

  3. [spoj104][Highways] (生成树计数+矩阵树定理+高斯消元)

    In some countries building highways takes a lot of time... Maybe that's because there are many possi ...

  4. BZOJ4031 [HEOI2015]小Z的房间 【矩阵树定理 + 高斯消元】

    题目链接 BZOJ4031 题解 第一眼:这不裸的矩阵树定理么 第二眼:这个模\(10^9\)是什么鬼嘛QAQ 想尝试递归求行列式,发现这是\(O(n!)\)的.. 想上高斯消元,却又处理不了逆元这个 ...

  5. CF917D-Stranger Trees【矩阵树定理,高斯消元】

    正题 题目链接:https://www.luogu.com.cn/problem/CF917D 题目大意 给出\(n\)个点的一棵树,对于每个\(k\)求有多少个\(n\)个点的树满足与给出的树恰好有 ...

  6. Wannafly Camp 2020 Day 1D 生成树 - 矩阵树定理,高斯消元

    给出两幅 \(n(\leq 400)\) 个点的无向图 \(G_1 ,G_2\),对于 \(G_1\) 的每一颗生成树,它的权值定义为有多少条边在 \(G_2\) 中出现.求 \(G_1\) 所有生成 ...

  7. 洛谷4208 JSOI2008最小生成树计数(矩阵树定理+高斯消元)

    qwq 这个题目真的是很好的一个题啊 qwq 其实一开始想这个题,肯定是无从下手. 首先,我们会发现,对于无向图的一个最小生成树来说,只有当存在一些边与内部的某些边权值相同的时候且能等效替代的时候,才 ...

  8. SP104 Highways (矩阵树,高斯消元)

    矩阵树定理裸题 //#include <iostream> #include <cstdio> #include <cstring> #include <al ...

  9. luoguP3317 [SDOI2014]重建 变元矩阵树定理 + 概率

    首先,我们需要求的是 $$\sum\limits_{Tree} \prod\limits_{E \in Tree} E(u, v) \prod\limits_{E \notin Tree} (1 - ...

随机推荐

  1. [SoapUI]怎样保存response到本地文件夹

    def myOutFile = "D:/AUS/Aspect Huntley feed URLs/Automation Save Responses/ahresearch.xml" ...

  2. Java Thread系列(一)线程创建

    Java Thread系列(一)线程创建 Java 中创建线程主要有三种方式:继承 Thread.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实 ...

  3. .net手动编写Windows服务

    1,打开VS,新建一个windows服务程序.项目名称自定义,我这里用的默认名称:Service1 2,打开Service1,按F7查看代码.代码里有三个方法:public Service1().pr ...

  4. PHP(十二)文件操作

  5. 三种调用WCF服务的代码

    wsHttpBinding,Massage UserName认证 static void Main(string[] args) { //使用服务引用方式生成的Client调用服务,使用自动生成的配置 ...

  6. 使用Nmap攻击靶机和使用Wireshark进行嗅探、分析

    使用Nmap攻击靶机和使用Wireshark进行嗅探.分析 在上一次课中已经对Nmap的使用.原理已经做了很多学习,这次的课更多地针对Wireshark进行学习和使用. 使用192.168.200.1 ...

  7. java并发编程实战:第十四章----构建自定义的同步工具

    一.状态依赖性管理 对于单线程程序,某个条件为假,那么这个条件将永远无法成真 在并发程序中,基于状态的条件可能会由于其他线程的操作而改变 可阻塞的状态依赖操作的结构 acquire lock on o ...

  8. 23 DesignPatterns学习笔记:C++语言实现 --- 2.4 Composite

    23 DesignPatterns学习笔记:C++语言实现 --- 2.4 Composite 2016-07-22 (www.cnblogs.com/icmzn) 模式理解

  9. B-spline Curves 学习前言与动机(1)

    B-spline Curves 学习之前言 本博客转自前人的博客的翻译版本,前几章节是原来博主的翻译内容,但是后续章节博主不在提供翻译,后续章节我在完成相关的翻译学习. (原来博客网址:http:// ...

  10. Android-Camera+SurfaceView

    Camera相机是属于硬件,每台设备的Camera硬件配置的参数都是不一样的,Camera通常是用来拍照,扫描二维码等等 AndroidManifest.xml配置Camera需要的权限: <! ...