联合省选 2020 作业题

题面
根据欧拉函数的性质,有 n = ∑ i ∣ n φ ( i ) n=\sum_{i|n}\varphi(i) n=inφ(i),因此,将最终要求的答案化为:
∑ T ∑ i ∈ T w i ∑ d = 1 φ ( d ) [ d ∣ g c d j ∈ T w j ] \sum_T \sum_{i \in T} w_i \sum_{d=1} \varphi(d)[d|gcd_{j \in T}w_j] TiTwid=1φ(d)[dgcdjTwj]
其中 g c d i ∈ S w i gcd_{i \in S}w_i gcdiSwi 为边集 S S S 中边权值的最大公约数。
对式子进行进一步整理,得到:
∑ d = 1 φ ( d ) ∑ T ∑ i ∈ T w i ∏ j ∈ T [ d ∣ w j ] \sum_{d=1} \varphi(d) \sum_T \sum_{i \in T} w_i \prod_{j \in T}[d|w_j] d=1φ(d)TiTwijT[dwj]
那么很明显,先枚举 d d d,然后将边权为 d d d 倍数的边提出来,跑矩阵树。设已经枚举了 d d d,得到的边集为 S S S,则要求:
∑ T ⊆ S ∑ i ∈ T w i \sum_{T \subseteq S} \sum_{i \in T} w_i TSiTwi
普通的矩阵树只能处理生成树边权积之和问题,即:
∑ T ∏ i ∈ T w i \sum_T \prod_{i \in T} w_i TiTwi
这里将矩阵中的元素换为一次多项式,边权为 w w w 的边在填入矩阵时改为 1 + w x 1+wx 1+wx,若行列式算出来的多项式为 ∑ i = 0 a i x i \sum_{i=0} a_i x^i i=0aixi,则所以生成树边权之和为 a 1 a_1 a1
由于改变了边权的形式,要求的结果也变为了:
∑ T ⊆ S ∏ i ∈ T ( 1 + w i x ) \sum_{T \subseteq S} \prod_{i \in T}(1+w_i x) TSiT(1+wix)
将多项式乘法展开,只对一次项求和,得到:
∑ T ⊆ S ∑ i ∈ T w i \sum_{T \subseteq S} \sum_{i \in T}w_i TSiTwi
由于只对一次项求和,因此在做多项式乘法和多项式逆元时对 x 2 x^2 x2 取模。
设现有多项式 ( a + b x ) (a+bx) (a+bx),要求其逆元。若 a = 0 a=0 a=0,则无逆元,同时生成树的答案为 0 0 0。否则,则一定有逆元 ( c + d x ) (c+dx) (c+dx) 满足 ( a + b x ) ( c + d x ) m o d    x 2 = 1 (a+bx)(c+dx) \mod x^2 =1 (a+bx)(c+dx)modx2=1。将其展开,得到:
a c + ( a d + b c ) x = 1 ac + (ad+bc)x = 1 ac+(ad+bc)x=1
因此:
{ a c = 1 a d + b c = 0 \begin{cases} ac=1 \\ ad+bc=0 \end{cases} {ac=1ad+bc=0
解得:
{ c = a − 1 d = − a − 2 b \begin{cases} c=a^{-1} \\ d=-a^{-2}b \end{cases} {c=a1d=a2b
按照这样的多项式运算法则跑普通矩阵树即可。
当边集 S S S 中没有生成树时可以剪枝。
时间复杂度 O ( n 3 w ) O(n^3w) O(n3w),空间复杂度 O ( n 2 + w ) O(n^2+w) O(n2+w)

#include<stdio.h>
#include<vector>
using namespace std;
#define R register int
#define L long long
#define I inline
#define F friend operator
#define N 152502
#define P 998244353
vector<int>G[N];
int phi[N],prime[14066],f[31],st[435],ed[435],l[435];
bool vis[N];
I int GetF(int x){
	if(f[x]==x){
		return x;
	}
	f[x]=GetF(f[x]);
	return f[x];
}
I void InitPhi(){
	phi[1]=1;
	int ct=0;
	for(R i=2;i!=N;i++){
		if(vis[i]==false){
			phi[i]=i-1;
			prime[ct]=i;
			ct++;
		}
		for(R j=0;j!=ct&&prime[j]*i<N;j++){
			int t=i*prime[j];
			vis[t]=true;
			if(i%prime[j]==0){
				phi[t]=phi[i]*prime[j];
				break;
			}
			phi[t]=phi[i]*(prime[j]-1);
		}
	}
}
I int Add(int x,int y){
	x+=y;
	if(x>=P){
		return x-P;
	}
	return x<0?x+P:x;
}
I int PowMod(int x,int y){
	int res=1;
	while(y!=0){
		if((y&1)==1){
			res=(L)res*x%P;
		}
		x=(L)x*x%P;
		y>>=1;
	}
	return res;
}
struct Poly{
	int A,B;
	I void Init(int x,int y){
		A=x;
		B=y;
	}
	I bool F==(Poly x,Poly y){
		return x.A==y.A&&x.B==y.B;
	}
	I Poly F+(Poly x,Poly y){
		Poly res;
		res.Init(Add(x.A,y.A),Add(x.B,y.B));
		return res;
	}
	I Poly F-(Poly x,Poly y){
		Poly res;
		res.Init(Add(x.A,-y.A),Add(x.B,-y.B));
		return res;
	}
	I Poly F*(Poly x,Poly y){
		Poly res;
		res.Init((L)x.A*y.A%P,((L)x.A*y.B+(L)x.B*y.A)%P);
		return res;
	}
}b[31][31];
I Poly Pair(int x,int y){
	Poly res;
	res.A=x;
	res.B=y;
	return res;
}
I Poly GetInv(Poly x){
	int tem=PowMod(x.A,P-2);
	return Pair(tem,P-(L)x.B*tem%P*tem%P);
}
I int GetAns(int&n){
	Poly res=Pair(1,0);
	for(R i=1;i!=n;i++){
		int p=i;
		while(p!=n&&b[p][i].A==0){
			p++;
		}
		if(p==n){
			return 0;
		}
		if(p!=i){
			for(R j=i;j!=n;j++){
				Poly tem=b[i][j];
				b[i][j]=b[p][j];
				b[p][j]=tem;
			}
			res=Pair(0,0)-res;
		}
		Poly inv=GetInv(b[i][i]);
		res=res*b[i][i];
		for(R j=i+1;j!=n;j++){
			Poly tem=inv*b[j][i];
			for(R k=i;k!=n;k++){
				b[j][k]=b[j][k]-tem*b[i][k];
			}
		}
	}
	return res.B;
}
int main(){
	InitPhi();
	int n,m,ans=0;
	scanf("%d%d",&n,&m);
	for(int i=0;i!=m;i++){
		scanf("%d%d%d",st+i,ed+i,l+i);
		for(R j=1;j*j<=l[i];j++){
			if(l[i]%j==0){
				G[j].push_back(i);
				if(j*j!=l[i]){
					G[l[i]/j].push_back(i);
				}
			}
		}
	}
	for(R i=1;i!=N;i++){
		if(G[i].size()>n-2){
			for(R i=1;i<=n;i++){
				f[i]=i;
				for(R j=1;j!=n;j++){
					b[i][j]=Pair(0,0);
				}
			}
			for(vector<int>::iterator T=G[i].begin();T!=G[i].end();T++){
				int x=st[*T],y=ed[*T];
				if(GetF(x)!=GetF(y)){
					f[f[x]]=y;
				}
				Poly tem=Pair(1,l[*T]);
				b[x][x]=b[x][x]+tem;
				b[y][y]=b[y][y]+tem;
				b[x][y]=b[x][y]-tem;
				b[y][x]=b[y][x]-tem;
			}
			bool tag=true;
			for(R i=2;i<=n;i++){
				if(GetF(i)!=GetF(1)){
					tag=false;
					break;
				}
			}
			if(tag==true){
				ans=Add(ans,(L)GetAns(n)*phi[i]%P);
			}
		}
	}
	printf("%d",ans);
	return 0;
}

另外,此题利用莫比乌斯反演也是可以做的。将题目要求的答案化为:
∑ d = 1 d ∑ T [ g c d i ∈ T w i = d ] ∑ j ∈ T w j \sum_{d=1} d \sum_T [gcd_{i \in T}w_i=d] \sum_{j \in T}w_j d=1dT[gcdiTwi=d]jTwj
= ∑ d = 1 d ∑ T ∏ i ∈ T [ d ∣ w i ] ∑ t = 1 ∏ j ∈ T [ t d ∣ w j ] μ ( t ) ∑ k ∈ T w k =\sum_{d=1} d \sum_T \prod_{i \in T}[d|w_i] \sum_{t=1} \prod_{j \in T}[td|w_j] \mu(t) \sum_{k \in T}w_k =d=1dTiT[dwi]t=1jT[tdwj]μ(t)kTwk
= ∑ d = 1 d ∑ T ∑ t = 1 μ ( t ) ∏ i ∈ T [ t d ∣ w i ] ∑ j ∈ T w j =\sum_{d=1}d \sum_T \sum_{t=1} \mu(t) \prod_{i \in T}[td|w_i] \sum_{j \in T}w_j =d=1dTt=1μ(t)iT[tdwi]jTwj
= ∑ p = 1 ∑ d ∣ p d μ ( p d ) ∑ T ∏ i ∈ T [ p ∣ w i ] ∑ j ∈ T w j =\sum_{p=1} \sum_{d|p} d \mu(\frac{p}{d}) \sum_T \prod_{i \in T}[p|w_i] \sum_{j \in T}w_j =p=1dpdμ(dp)TiT[pwi]jTwj
利用这个式子,再结合矩阵树即可。时空复杂度与上一个算法相同。
根据狄利克雷卷积可知 φ ( n ) = ∑ d ∣ n n d μ ( d ) \varphi(n)=\sum_{d|n}\frac{n}{d}\mu(d) φ(n)=dndnμ(d),所以最终的式子与之前的算法也是完全相同的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值