题面
根据欧拉函数的性质,有
n
=
∑
i
∣
n
φ
(
i
)
n=\sum_{i|n}\varphi(i)
n=∑i∣nφ(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]
∑T∑i∈Twi∑d=1φ(d)[d∣gcdj∈Twj]
其中
g
c
d
i
∈
S
w
i
gcd_{i \in S}w_i
gcdi∈Swi 为边集
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)∑T∑i∈Twi∏j∈T[d∣wj]
那么很明显,先枚举
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
∑T⊆S∑i∈Twi
普通的矩阵树只能处理生成树边权积之和问题,即:
∑
T
∏
i
∈
T
w
i
\sum_T \prod_{i \in T} w_i
∑T∏i∈Twi
这里将矩阵中的元素换为一次多项式,边权为
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)
∑T⊆S∏i∈T(1+wix)
将多项式乘法展开,只对一次项求和,得到:
∑
T
⊆
S
∑
i
∈
T
w
i
\sum_{T \subseteq S} \sum_{i \in T}w_i
∑T⊆S∑i∈Twi
由于只对一次项求和,因此在做多项式乘法和多项式逆元时对
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=a−1d=−a−2b
按照这样的多项式运算法则跑普通矩阵树即可。
当边集
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=1d∑T[gcdi∈Twi=d]∑j∈Twj
=
∑
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=1d∑T∏i∈T[d∣wi]∑t=1∏j∈T[td∣wj]μ(t)∑k∈Twk
=
∑
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=1d∑T∑t=1μ(t)∏i∈T[td∣wi]∑j∈Twj
=
∑
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=1∑d∣pdμ(dp)∑T∏i∈T[p∣wi]∑j∈Twj
利用这个式子,再结合矩阵树即可。时空复杂度与上一个算法相同。
根据狄利克雷卷积可知
φ
(
n
)
=
∑
d
∣
n
n
d
μ
(
d
)
\varphi(n)=\sum_{d|n}\frac{n}{d}\mu(d)
φ(n)=∑d∣ndnμ(d),所以最终的式子与之前的算法也是完全相同的。