Description
给出一个
n
n
n个点
m
m
m条边的无向图,边有边权,对于这张图的任意一个有根生成树,定义其权值为
∑
e
∈
{
x
,
y
}
w
e
⋅
m
a
x
(
d
x
,
d
y
)
\sum\limits_{e\in \{x,y\}}w_e\cdot max(d_x,d_y)
e∈{x,y}∑we⋅max(dx,dy)
其中
d
x
d_x
dx为
x
x
x点在生成树中的深度,统计这张图所有有根生成树权值之和,结果模
1
0
9
+
7
10^9+7
109+7
Input
第一行输入两个整数 n , m n,m n,m表示点数和边数,之后 m m m行每行三个整数 x , y , z x,y,z x,y,z表示 x , y x,y x,y之间有一条权值为 x x x的边
( 1 ≤ n ≤ 12 , 1 ≤ m ≤ 1000 , 1 ≤ x , y ≤ n , 1 ≤ z ≤ 5000 ) (1\le n\le 12,1\le m\le 1000,1\le x,y\le n,1\le z\le 5000) (1≤n≤12,1≤m≤1000,1≤x,y≤n,1≤z≤5000)
Output
输出这张图所有有根生成树权值之和,结果模 1 0 9 + 7 10^9+7 109+7
Sample Input
4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 1
Sample Output
303
Solution
考虑每条边 e = ( u , v , w ) e=(u,v,w) e=(u,v,w)对答案的贡献,贡献分为两部分, u u u在生成树中是 v v v的父亲节点以及 v v v是 u u u的父亲节点,以 u u u是 v v v的父亲节点为例,断掉这条边将树分成两个连通块,记 v v v所在连通块为 T T T, u u u所在连通块为 S S S,此时需要求出以 v v v为根的子树方案数 g [ T ] [ v ] g[T][v] g[T][v],而 v v v节点的深度和实际上取决于 u u u在 S S S点集中的深度,以 f [ S ] [ u ] f[S][u] f[S][u]表示 u u u在 S S S状态的所有子树中的深度加一的和,那么我们只要求出 f , g f,g f,g,即得答案为 ∑ e ∈ E , S , T w ⋅ ( f [ S ] [ u ] ⋅ g [ T ] [ v ] + g [ S ] [ u ] ⋅ f [ T ] [ v ] ) \sum\limits_{e\in E,S,T}w\cdot (f[S][u]\cdot g[T][v]+g[S][u]\cdot f[T][v]) e∈E,S,T∑w⋅(f[S][u]⋅g[T][v]+g[S][u]⋅f[T][v])
现在考虑状压求
f
,
g
f,g
f,g,枚举点集
S
S
S和
S
S
S中一点
u
u
u,下面要把
S
S
S分解为两个不交的集合
T
,
W
T,W
T,W,因为涉及到计数,故要有顺序,否则会记重,令
T
T
T为包含
S
−
{
u
}
S-\{u\}
S−{u}点集中最小编号的集合,
W
W
W为
T
T
T在
S
S
S中的补,枚举
T
T
T中一点
v
v
v,考虑通过将
T
,
W
T,W
T,W通过边
u
↔
v
u\leftrightarrow v
u↔v合并进行对
S
S
S状态的转移,首先有转移
g
[
S
]
[
u
]
+
=
g
[
W
]
[
u
]
⋅
g
[
T
]
[
v
]
⋅
n
u
m
[
u
]
[
v
]
g[S][u]+=g[W][u]\cdot g[T][v]\cdot num[u][v]
g[S][u]+=g[W][u]⋅g[T][v]⋅num[u][v]
其中
n
u
m
[
u
]
[
v
]
num[u][v]
num[u][v]表示
u
↔
v
u\leftrightarrow v
u↔v边的数量,主要考虑求
f
[
S
]
[
u
]
f[S][u]
f[S][u],
u
↔
v
u\leftrightarrow v
u↔v边存在状态有两种:
1.
u
u
u是
v
v
v的父亲,那么此时
u
u
u的深度不变,有转移
f
[
S
]
[
u
]
+
=
f
[
W
]
[
u
]
⋅
g
[
T
]
[
v
]
⋅
n
u
m
[
u
]
[
v
]
f[S][u]+=f[W][u]\cdot g[T][v]\cdot num[u][v]
f[S][u]+=f[W][u]⋅g[T][v]⋅num[u][v]
2.
v
v
v是
u
u
u的父亲,此时
u
u
u的深度为
v
v
v的深度加一,
v
v
v的深度加一之和为
f
[
T
]
[
v
]
f[T][v]
f[T][v],每种方案的深度要再加一才是
u
u
u在这种方案下的深度,点集
T
T
T构成一棵树的方案数为
c
n
t
[
T
]
⋅
g
[
T
]
[
v
]
cnt[T]\cdot g[T][v]
cnt[T]⋅g[T][v],故有转移
f
[
S
]
[
u
]
+
=
(
f
[
T
]
[
v
]
+
c
n
t
[
T
]
⋅
g
[
T
]
[
v
]
)
⋅
g
[
W
]
[
u
]
⋅
n
u
m
[
u
]
[
v
]
f[S][u]+=(f[T][v]+cnt[T]\cdot g[T][v])\cdot g[W][u]\cdot num[u][v]
f[S][u]+=(f[T][v]+cnt[T]⋅g[T][v])⋅g[W][u]⋅num[u][v]
Code
#include<cstdio>
using namespace std;
typedef long long ll;
#define mod 1000000007
int mul(int x,int y)
{
ll z=1ll*x*y;
return z-z/mod*mod;
}
int add(int x,int y)
{
x+=y;
if(x>=mod)x-=mod;
return x;
}
#define maxn (1<<12)
int n,m,e[5005][3],cnt[maxn],num[13][13],f[maxn][13],g[maxn][13];
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);
e[i][0]--,e[i][1]--;
num[e[i][0]][e[i][1]]++,num[e[i][1]][e[i][0]]++;
}
int N=(1<<n)-1;
for(int i=1;i<=N;i++)cnt[i]=cnt[i/2]+(i&1);
for(int S=1;S<=N;S++)
for(int u=0;u<n;u++)
if((S>>u)&1)
{
int SS=S^(1<<u);
int temp=SS&(-SS);
if(!SS)
{
f[S][u]=g[S][u]=1;
continue;
}
for(int T=SS;;T=(T-1)&SS)
{
if(T&temp)
for(int v=0;v<n;v++)
if((T>>v)&1)
{
int W=S^T;
int res=add(mul(f[W][u],g[T][v]),mul(add(f[T][v],mul(cnt[T],g[T][v])),g[W][u]));
f[S][u]=add(f[S][u],mul(res,num[u][v]));
g[S][u]=add(g[S][u],mul(mul(g[W][u],g[T][v]),num[u][v]));
}
if(!T)break;
}
}
int ans=0;
for(int i=0;i<m;i++)
{
int u=e[i][0],v=e[i][1],w=e[i][2];
for(int S=1<<u;S<=N;S=(S+1)|(1<<u))
ans=add(ans,mul(w,add(mul(f[S][u],g[N^S][v]),mul(g[S][u],f[N^S][v]))));
}
printf("%d\n",ans);
return 0;
}