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

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

[(sum_{i=1}^{n-1}w_{e_i}) imes 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) 全相同,那就直接 ( ext{matrix}) 定理求一下生成树个数即可。但现在 (w_i) 不相同。
化一下式子:

[egin{aligned} (sum_{i=1}^{n-1}w_{e_i}) imes gcd(w_{e_1}, w_{e_2},cdots,w_{e_{n-1}}) \=(sum_{i=1}^{n-1}w_i{e_i}) imes 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) 相同的话,构造 ( ext{kirchhoff}) 矩阵时会在对应的位置加减1,现在就改成在对应位置加上 (1+w_ix)这样一个多项式,然后给矩阵求个行列式,行列式的一次项系数就是包含这条边的生成树个数乘 (w_i) 。所以我们一开给每条边都这样做,然后计算行列式时对 (x^2) 取模即可。
时间复杂度 (mathcal{O} (n^3sum_{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
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/akura/p/14577657.html