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

时间:2023-03-09 05:31:17
luogu 3790 文艺数学题 - 矩阵树定理 - 容斥原理

题目传送门

  戳我来传送

题目大意

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

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

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

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

  但是这么做会T掉。

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

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

Code

 /**
* luogu
* Problem#3790
* Accepted
* Time: 6016ms
* Memory: 9402k
*/
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef bool boolean;
#define ll long long const int N = , M = 1e9 + ; void exgcd(int a, int b, int& d, int& x, int& y) {
if(!b)
d = a, x = , y = ;
else {
exgcd(b, a % b, d, y, x);
y -= (a / b) * x;
}
} int inv(int a, int n) {
int d, x, y;
exgcd(a, n, d, x, y);
return (x < ) ? (x + n) : (x);
} typedef class Matrix {
public:
int a[N][N]; void reset() {
memset(a, , sizeof(a));
} int det(int n) {
int pro = ;
for (int i = , e; i <= n; i++) {
e = ;
for (int j = i; j <= n && !e; j++)
if (a[j][i])
e = j;
if (e == ) return ;
if (e != i)
for (int j = ; j <= n; j++)
swap(a[e][j], a[i][j]);
for (int j = , x, y; j <= n; j++) {
if (j == i) continue;
x = a[i][i], y = a[j][i], pro = pro * 1ll * x % M;
for (int k = ; k <= n; k++) {
a[j][k] = (a[j][k] * 1ll * x - a[i][k] * 1ll * y) % M;
if (a[j][k] < ) a[j][k] += M;
} }
}
int rt = inv(pro, M);
for (int i = ; i <= n; i++)
rt = rt * 1ll * a[i][i] % M;
return rt;
}
}Matrix; typedef class Edge {
public:
int u, v, w;
}Edge; const int Val = 1e6 + ; int n, m;
Edge* es;
Matrix a;
int ce[Val], f[Val]; inline void init() {
scanf("%d%d", &n, &m);
es = new Edge[(m + )];
for (int i = ; i <= m; i++)
scanf("%d%d%d", &es[i].u, &es[i].v, &es[i].w), ce[es[i].w]++;
} int calc(int g) {
if (ce[g] < n - ) return ;
a.reset();
for (int i = , u, v; i <= m; i++)
if (!(es[i].w % g)) {
u = es[i].u - , v = es[i].v - ;
a.a[u][u]++, a.a[v][v]++;
a.a[u][v]--, a.a[v][u]--;
if (a.a[u][v] < ) a.a[u][v] += M;
if (a.a[v][u] < ) a.a[v][u] += M;
}
int rt = a.det(n - );
// cerr << rt << endl;
return rt;
} int res = ;
inline void solve() {
for (int i = ; i < Val; i++)
for (int j = (i << ); j < Val; j += i)
ce[i] += ce[j];
for (int i = Val - ; i; i--) {
f[i] = calc(i);
for (int j = (i << ); j < Val; j += i) {
f[i] -= f[j];
if (f[i] < )
f[i] += M;
}
res = (res + f[i] * 1ll * i) % M;
}
printf("%d", res);
} int main() {
init();
solve();
return ;
}