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

Posted 阿波罗2003

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }

 

以上是关于luogu 3790 文艺数学题 - 矩阵树定理 - 容斥原理的主要内容,如果未能解决你的问题,请参考以下文章

模板—数学—矩阵树定理

luogu4430 小猴打架

[luogu3244 SHOI2016] 黑暗前的幻想乡(容斥原理+矩阵树定理)

BZOJ4766: 文艺计算姬

luogu P3391 文艺平衡树

[luogu P3391] 文艺平衡树