luogu 3790 文艺数学题 - 矩阵树定理 - 容斥原理

题目传送门

  戳我来传送

题目大意

  给定一个图,问它的所有生成树的边权的最大公约数之和。

  可以考虑计算边权的最大公约数为$i$的生成树的个数$f(i)$,最后累加答案。

  然后考虑这样的生成树的个数怎么求,根据某个经典套路,我们可以容斥。

  因为可以求出边权的最大公约数为$i$的倍数的生成树的个数$F(i)$,所以减去它的倍数的$f$就是$f(i)$了。

  但是这么做会T掉。

  可以用$O(W\log W)$的时间内预处理出为边权$i$的倍数的边数有多少条。然后高消前判断一下边数是否大于等于$n - 1$。

  具体有关时间复杂度的证明可以在洛谷的题解中找到,这里就不给出了。

Code

  1 /**
  2  * luogu
  3  * Problem#3790
  4  * Accepted
  5  * Time: 6016ms
  6  * Memory: 9402k
  7  */
  8 #include <iostream>
  9 #include <cstring>
 10 #include <cstdio>
 11 using namespace std;
 12 typedef bool boolean;
 13 #define ll long long
 14
 15 const int N = 65, M = 1e9 + 7;
 16
 17 void exgcd(int a, int b, int& d, int& x, int& y) {
 18     if(!b)
 19         d = a, x = 1, y = 0;
 20     else {
 21         exgcd(b, a % b, d, y, x);
 22         y -= (a / b) * x;
 23     }
 24 }
 25
 26 int inv(int a, int n) {
 27     int d, x, y;
 28     exgcd(a, n, d, x, y);
 29     return (x < 0) ? (x + n) : (x);
 30 }
 31
 32 typedef class Matrix {
 33     public:
 34         int a[N][N];
 35
 36         void reset() {
 37             memset(a, 0, sizeof(a));
 38         }
 39
 40         int det(int n) {
 41             int pro = 1;
 42             for (int i = 1, e; i <= n; i++) {
 43                 e = 0;
 44                 for (int j = i; j <= n && !e; j++)
 45                     if (a[j][i])
 46                         e = j;
 47                 if (e == 0)    return 0;
 48                 if (e != i)
 49                     for (int j = 1; j <= n; j++)
 50                         swap(a[e][j], a[i][j]);
 51                 for (int j = 1, x, y; j <= n; j++) {
 52                     if (j == i)    continue;
 53                     x = a[i][i], y = a[j][i], pro = pro * 1ll * x % M;
 54                     for (int k = 1; k <= n; k++) {
 55                         a[j][k] = (a[j][k] * 1ll * x - a[i][k] * 1ll * y) % M;
 56                         if (a[j][k] < 0)    a[j][k] += M;
 57                     }
 58
 59                 }
 60             }
 61             int rt = inv(pro, M);
 62             for (int i = 1; i <= n; i++)
 63                 rt = rt * 1ll * a[i][i] % M;
 64             return rt;
 65         }
 66 }Matrix;
 67
 68 typedef class Edge {
 69     public:
 70         int u, v, w;
 71 }Edge;
 72
 73 const int Val = 1e6 + 1;
 74
 75 int n, m;
 76 Edge* es;
 77 Matrix a;
 78 int ce[Val], f[Val];
 79
 80 inline void init() {
 81     scanf("%d%d", &n, &m);
 82     es = new Edge[(m + 1)];
 83     for (int i = 1; i <= m; i++)
 84         scanf("%d%d%d", &es[i].u, &es[i].v, &es[i].w), ce[es[i].w]++;
 85 }
 86
 87 int calc(int g) {
 88     if (ce[g] < n - 1)    return 0;
 89     a.reset();
 90     for (int i = 1, u, v; i <= m; i++)
 91         if (!(es[i].w % g)) {
 92             u = es[i].u - 1, v = es[i].v - 1;
 93             a.a[u][u]++, a.a[v][v]++;
 94             a.a[u][v]--, a.a[v][u]--;
 95             if (a.a[u][v] < 0)    a.a[u][v] += M;
 96             if (a.a[v][u] < 0)    a.a[v][u] += M;
 97         }
 98     int rt = a.det(n - 1);
 99 //    cerr << rt << endl;
100     return rt;
101 }
102
103 int res = 0;
104 inline void solve() {
105     for (int i = 1; i < Val; i++)
106         for (int j = (i << 1); j < Val; j += i)
107             ce[i] += ce[j];
108     for (int i = Val - 1; i; i--) {
109         f[i] = calc(i);
110         for (int j = (i << 1); j < Val; j += i) {
111             f[i] -= f[j];
112             if (f[i] < 0)
113                 f[i] += M;
114         }
115         res = (res + f[i] * 1ll * i) % M;
116     }
117     printf("%d", res);
118 }
119
120 int main() {
121     init();
122     solve();
123     return 0;
124 }

原文地址:https://www.cnblogs.com/yyf0309/p/8492493.html

时间: 2024-11-02 16:09:32

luogu 3790 文艺数学题 - 矩阵树定理 - 容斥原理的相关文章

【Luogu】P3317重建(高斯消元+矩阵树定理)

题目链接 因为这个专门跑去学了矩阵树定理和高斯消元qwq 不过不是很懂.所以这里只放题解 玫葵之蝶的题解 某未知dalao的矩阵树定理 代码 #include<cstdio> #include<cstdlib> #include<cctype> #include<algorithm> #include<cstring> #include<cmath> #define eps 1e-8 #define maxn 100 using na

bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥)

bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥) bzoj Luogu 题解时间 看一看数据范围,求生成树个数毫无疑问直接上矩阵树定理. 但是要求每条边都属于不同公司就很难直接实现. 按套路上容斥: 如果直接将几个公司的修路列表加进矩阵里的话,求出来的是"只使用"这些边的生成树个数. 很明显上容斥之后就会直接变成"只使用"且"每个都被使用"的个数. 正好符合题目要求的生成树的n-1条边分属于n-1个公

矩阵树定理速证

凯莱公式: spanning_trees_num( G ) = spanning_trees_num( G - e ) + spanning_trees_num( G · e ) 矩阵树定理: G 相应的拉普拉斯矩阵(度矩阵 - 邻接矩阵)L( G )   删除随意一行一列得到的行列式的值det( L*( G ) ) 即生成树的个数,即spanning_trees_num( G ) = det( L*( G ) ) 证: 归纳如果 spanning_trees_num( G - e ) = de

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

In some countries building highways takes a lot of time... Maybe that's because there are many possiblities to construct a network of highways and engineers can't make up their minds which one to choose. Suppose we have a list of cities that can be c

CSU 1805 Three Capitals(矩阵树定理+Best定理)

http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1805 题意: A和B之间有a条边,A和G之间有b条边,B和G之间有c条边.现在从A点出发走遍所有的边,然后再回到A点,问一共有多少种方法. 思路: 16年湖南省赛题目,这道题目是求欧拉回路的个数,和生成树的计数有一定的联系. 首先给出神奇的Best定理,这是什么鬼定理,反正查不到什么有关该定理的文章... $ec(G)=t_s(G)\cdot deg(s)! \cdot \prod_{v\i

【BZOJ4031】[HEOI2015]小Z的房间 矩阵树定理

[BZOJ4031][HEOI2015]小Z的房间 Description 你突然有了一个大房子,房子里面有一些房间.事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子.在一开始的时候,相邻的格子之间都有墙隔着. 你想要打通一些相邻房间的墙,使得所有房间能够互相到达.在此过程中,你不能把房子给打穿,或者打通柱子(以及柱子旁边的墙).同时,你不希望在房子中有小偷的时候会很难抓,所以你希望任意两个房间之间都只有一条通路.现在,你希望统计一共有多少种可行的方案.

【bzoj2467】[中山市选2010]生成树 矩阵树定理

题目描述 有一种图形叫做五角形圈.一个五角形圈的中心有1个由n个顶点和n条边组成的圈.在中心的这个n边圈的每一条边同时也是某一个五角形的一条边,一共有n个不同的五角形.这些五角形只在五角形圈的中心的圈上有公共的顶点.如图0所示是一个4-五角形圈. 现在给定一个n五角形圈,你的任务就是求出n五角形圈的不同生成树的数目.还记得什么是图的生成树吗?一个图的生成树是保留原图的所有顶点以及顶点的数目减去一这么多条边,从而生成的一棵树. 注意:在给定的n五角形圈中所有顶点均视为不同的顶点. 输入 输入包含多

【bzoj4894】天赋 矩阵树定理

题目描述 小明有许多潜在的天赋,他希望学习这些天赋来变得更强.正如许多游戏中一样,小明也有n种潜在的天赋,但有一些天赋必须是要有前置天赋才能够学习得到的.也就是说,有一些天赋必须是要在学习了另一个天赋的条件下才能学习的.比如,要想学会"开炮",必须先学会"开枪".一项天赋可能有多个前置天赋,但只需习得其中一个就可以学习这一项天赋.上帝不想为难小明,于是小明天生就已经习得了1号天赋-----"打架".于是小明想知道学习完这n种天赋的方案数,答案对1

【整合】矩阵树定理模板

矩阵树定理求生成树计数模板. 原题是SPOJhighways 代码又长又丑- #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define MAXN 20 #define eps 1e-9 using namespace std; int A[MAXN][MAXN],D[MAXN][MAXN];//A是邻接矩阵D是