[省选联考 2020 A 卷] 作业题

给定一张 \(n\) 个点 \(m\) 条边的带边权的无向图,定义一棵生成树 \(T\) 的价值为:

\[(\sum_{i=1}^{n-1}w_{e_i}) \times \gcd(w_{e_1}, w_{e_2},\cdots,w_{e_{n-1}}) \]

其中 \(w_{e_i}\)\(T\) 中的边。要求求出所有生成树的价值和。\(1 \leq n \leq 30 , 1 \leq m \leq \frac{n(n-1)}{2} , 1 \leq w_i \leq 152501\) ,无重边自环。


怎么想到的啊。。。
首先如果 \(w_i\) 全相同,那就直接 \(\text{matrix}\) 定理求一下生成树个数即可。但现在 \(w_i\) 不相同。
化一下式子:

\[\begin{aligned} (\sum_{i=1}^{n-1}w_{e_i}) \times \gcd(w_{e_1}, w_{e_2},\cdots,w_{e_{n-1}}) \\=(\sum_{i=1}^{n-1}w_i{e_i}) \times \sum_{d|w_1, d|w_2, \cdots ,d|w_{n-1}}\varphi(d) \\=\sum_{d=1}\varphi(d) \sum_{d|w_1,d|w_2,\cdots,w_{n-1}}\sum_{i=1}^{n-1}w_i \end{aligned} \]

现在就只需要对于每个 \(d\) ,求出所有边权是 \(d\) 的倍数的边构成的子图的答案。考虑对每条边计算它的贡献,也就是对于这条边来说,有多少棵包含它的生成树。本来如果 \(w_i\) 相同的话,构造 \(\text{kirchhoff}\) 矩阵时会在对应的位置加减1,现在就改成在对应位置加上 \(1+w_ix\)这样一个多项式,然后给矩阵求个行列式,行列式的一次项系数就是包含这条边的生成树个数乘 \(w_i\) 。所以我们一开给每条边都这样做,然后计算行列式时对 \(x^2\) 取模即可。
时间复杂度 \(\mathcal{O} (n^3\sum_{i=1}^{maxw}\sigma_0(i))\)

#include<bits/stdc++.h>
#define rg register
#define il inline
#define cn const
#define gc getchar()
#define fp(i,a,b) for(rg int i=(a),ed=(b);i<=ed;++i)
#define fb(i,a,b) for(rg int i=(a),ed=(b);i>=ed;--i)
#define go(u) for(rg int i=head[u];~i;i=e[i].nxt)
using namespace std;
typedef cn int cint;
typedef long long LL;
il void rd(int &x){
    x = 0;
	rg int f(1); rg char c(gc);
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=gc;}
	while('0'<=c&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
	x*=f;
}
cint maxn = 31, maxm = 440, maxw = 152510, mod = 998244353;
int n, m, mx, ans;
int pri[maxw], cnt, isnp[maxw], phi[maxw];
int tot, id[maxm];
il int fpow(int a, int b, int ans = 1){
	for(; b; b >>= 1, a = 1ll*a*a%mod)if(b&1)ans = 1ll*ans*a%mod;
	return ans;
}
struct node{int fi, se;}kir[maxn][maxn];
il node operator + (cn node &x, cn node &y){return (node){(x.fi+y.fi)%mod, (x.se+y.se)%mod};}
il node operator - (cn node &x, cn node &y){return (node){(x.fi-y.fi+mod)%mod, (x.se-y.se+mod)%mod};}
il node operator * (cn node &x, cn node &y){return (node){1ll*x.fi*y.fi%mod, (1ll*x.fi*y.se+1ll*x.se*y.fi)%mod};}
il node operator / (cn node &x, cn node &y){
	rg int inv = fpow(y.fi, mod-2), sinv = 1ll*inv*inv%mod;
	return (node){1ll*x.fi*inv%mod, (1ll*x.se*y.fi%mod-1ll*x.fi*y.se%mod+mod)*sinv%mod};
}
struct edge{int u, v, w;}e[maxm];
il void SWAP(node *a, node *b){fp(i, 1, n)swap(a[i], b[i]);}
il int matrix_tree(){
	if(tot < n-1)return 0;
	node ans = (node){1, 0};
	memset(kir, 0, sizeof kir);
	fp(i, 1, tot){
		++kir[e[id[i]].u][e[id[i]].u].fi, ++kir[e[id[i]].v][e[id[i]].v].fi;
		--kir[e[id[i]].u][e[id[i]].v].fi, --kir[e[id[i]].v][e[id[i]].u].fi;
		kir[e[id[i]].u][e[id[i]].u].se += e[id[i]].w, kir[e[id[i]].v][e[id[i]].v].se += e[id[i]].w;
		kir[e[id[i]].u][e[id[i]].v].se -= e[id[i]].w, kir[e[id[i]].v][e[id[i]].u].se -= e[id[i]].w;
	}
	fp(i, 1, n)fp(j, 1, n)kir[i][j] = (node){(kir[i][j].fi+mod)%mod, (kir[i][j].se+mod)%mod};
	fp(i, 2, n){
		if(!kir[i][i].fi){
			fp(j, i+1, n)if(kir[j][i].fi){
				ans = (node){mod-ans.fi, mod-ans.se}, SWAP(kir[i], kir[j]);
				break;
			}
		}
		rg node mlt;
		fp(j, i+1, n){
			mlt = kir[j][i]/kir[i][i];
			fp(k, i, n)kir[j][k] = kir[j][k]-kir[i][k]*mlt;
		}
		ans = ans*kir[i][i];
	}
	return ans.se;
}
int main(){
	rd(n), rd(m);
	fp(i, 1, m)rd(e[i].u), rd(e[i].v), rd(e[i].w), mx = max(mx, e[i].w);
	phi[1] = 1;
	fp(i, 2, mx){
		if(!isnp[i])pri[++cnt] = i, phi[i] = i-1;
		for(rg int j = 1; j <= cnt && pri[j]*i <= mx; ++j){
			isnp[i*pri[j]] = 1;
			if(i%pri[j] == 0){phi[i*pri[j]] = phi[i]*pri[j]; break;}
			phi[i*pri[j]] = phi[i]*phi[pri[j]];
		}
	}
	fp(i, 1, mx){
		tot = 0;
		fp(j, 1, m)if(e[j].w % i == 0)id[++tot] = j;
		ans = (ans+1ll*phi[i]*matrix_tree())%mod;
	}
	printf("%d\n", ans);
	return 0;
}
posted @ 2021-03-25 15:27  修电缆的建筑工  阅读(62)  评论(0编辑  收藏  举报