题意
给一张带权无向图,定义一颗生成树的权值为树上的边在三进制下不进位加法的和。求所有生成树的权值的和。
题解
第一时间应该想到矩阵树。
然后考虑去定义
A
×
B
=
A
+
B
A \times B= A +B
A×B=A+B(新定义)。
可以发现若将其表示成多项式形式 F ( x ) F(x) F(x),那么两个权值在三进制下加法的值,就是他们多项式乘积的项次 m o d mod mod 3 3 3。
所以考虑拆位。为
i
i
i 则表示
f
[
i
]
=
1
f[i]=1
f[i]=1。所以
A
×
B
=
F
A
(
x
)
×
F
B
(
x
)
(
m
o
d
x
3
−
1
)
A \times B=F_A(x) \times F_B(x) \pmod{x^3-1}
A×B=FA(x)×FB(x)(modx3−1) 中为
1
1
1 的项次。
我们注意到
F
A
(
x
)
×
F
B
(
x
)
(
m
o
d
x
3
−
1
)
F_A(x) \times F_B(x) \pmod{x^3-1}
FA(x)×FB(x)(modx3−1) 其实就是
F
A
(
x
)
F_A(x)
FA(x) 和
F
B
(
x
)
F_B(x)
FB(x) 在三次下的
F
F
T
FFT
FFT ,所以我们用复数
w
3
1
=
(
−
1
2
,
3
2
)
w_3^1=(-\frac{1}{2},\frac{\sqrt{3}}{2})
w31=(−21,23) 当做单位根。把点值求出来,用点值求行列式,最后再乘以转移逆矩阵即可求出系数
s
0
,
s
1
,
s
2
s_0,s_1,s_2
s0,s1,s2 ,答案即为
s
1
+
2
×
s
2
s_1+2 \times s_2
s1+2×s2 。
题解
#include<bits/stdc++.h>
using namespace std;
const int N=105;
const int P=1e9+7;
const int G=917937628;
int n,m,st[N*N],ed[N*N],c[N*N],f[N][N][3],A;
struct Node{
int a,b;
}z[N][N],s[3],g[N][N],ans[3],W[3];
inline int ksm(int a,int b){
int s=1;while(b){
if(b&1)s=1ll*s*a%P;
a=1ll*a*a%P;b>>=1;
}
return s;
}
Node operator *(const Node x,const Node y){
Node c;
c.a=((1ll*x.a*y.a-1ll*x.b*y.b)%P+P)%P;
c.b=(1ll*x.a*y.b+1ll*x.b*y.a)%P;
// cout<<"CHneg "<<x.a<<" "<<x.b<<" "<<y.a<<" "<<y.b<<" "<<c.a<<" "<<c.b<<endl;
return c;
}
Node operator +(const Node x,const Node y){return (Node){(x.a+y.a)%P,(y.b+x.b)%P};}
Node operator -(const Node x,const Node y){return (Node){(x.a-y.a+P)%P,(x.b-y.b+P)%P};}
Node operator /(const Node x,const Node y){
int inv=ksm((1ll*y.a*y.a+1ll*y.b*y.b)%P,P-2);
Node c=(Node){1ll*y.a*inv%P,(P-1ll*y.b*inv%P)%P};
return x*c;
}
inline Node det(){
Node kp=(Node){1,0};
for(int i=1;i<n;++i){
for(int j=i+1;j<n;++j){
if((z[j][i].a!=0||z[j][i].b!=0)){
swap(z[i],z[j]);
// cout<<"zhuan "<<i<<" "<<j<<" "<<z[i][i].a<<" "<<z[i][i].b<<endl;
kp=(Node){0,0}-kp;
break;
}
}
// if(!z[i][i].a&&!z[i][i].b)return (Node){-1,0};
for(int j=i+1;j<n;++j){
Node bei=z[j][i]/z[i][i];
for(int k=i;k<n;++k)z[j][k]=z[j][k]-z[i][k]*bei;
}
}
for(int i=1;i<n;++i)kp=kp*z[i][i];
return kp;
}
inline int solve(){
for(int k=0;k<3;++k){
for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)z[i][j]=(Node){0,0};
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
for(int Z=0;Z<3;++Z)z[i][j]=z[i][j]+(Node){(f[i][j][Z]+P)%P,0}*W[Z*k%3];
}
}
//for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)cout<<"JUzhen "<<i<<" "<<j<<" "<<z[i][j].a<<" "<<z[i][j].b<<endl;
s[k]=det();
if(s[k].a==-1)return 0;
}
ans[1]=W[0]*s[0]+W[2]*s[1]+W[1]*s[2];
ans[2]=W[0]*s[0]+W[1]*s[1]+W[2]*s[2];
return (ans[1].a+2ll*ans[2].a)%P*ksm(3,P-2)%P;
}
int main(){
/* freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);*/
Node Wn=(Node){P-ksm(2,P-2),1ll*G*ksm(2,P-2)%P};
W[0]=(Node){1,0};W[1]=Wn;W[2]=Wn*Wn;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)scanf("%d%d%d",&st[i],&ed[i],&c[i]);
for(int k=0,kp=1;k<=12;++k,kp*=3){
for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)for(int Z=0;Z<3;++Z)f[i][j][Z]=0;
for(int i=1;i<=m;++i){
int a=st[i],b=ed[i],Z=(c[i]/kp)%3;
f[a][a][Z]++;f[b][b][Z]++;
f[a][b][Z]--;f[b][a][Z]--;
}
int su=solve();
A=(A+1ll*kp*su)%P;
}
printf("%d\n",A);
return 0;
}