1. 程式人生 > >Newcoder 147 B.Enumeration not optimization(狀壓DP+樹形DP)

Newcoder 147 B.Enumeration not optimization(狀壓DP+樹形DP)

Description

給出一個nn個點mm條邊的無向圖,邊有邊權,對於這張圖的任意一個有根生成樹,定義其權值為 e{x,y}wemax(dx,dy) \sum\limits_{e\in \{x,y\}}w_e\cdot max(d_x,d_y) 其中dxd_xxx點在生成樹中的深度,統計這張圖所有有根生成樹權值之和,結果模109+710^9+7

Input

第一行輸入兩個整數n,mn,m表示點數和邊數,之後mm行每行三個整數x,y,zx,y,z表示x,yx,y之間有一條權值為xx的邊

(1

n12,1m1000,1x,yn,1z5000)(1\le n\le 12,1\le m\le 1000,1\le x,y\le n,1\le z\le 5000)

Output

輸出這張圖所有有根生成樹權值之和,結果模109+710^9+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)對答案的貢獻,貢獻分為兩部分,uu在生成樹中是vv的父親節點以及vvuu的父親節點,以u

uvv的父親節點為例,斷掉這條邊將樹分成兩個連通塊,記vv所在連通塊為TTuu所在連通塊為SS,此時需要求出以vv為根的子樹方案數g[T][v]g[T][v],而vv節點的深度和實際上取決於uuSS點集中的深度,以f[S][u]f[S][u]表示uuSS狀態的所有子樹中的深度加一的和,那麼我們只要求出f,gf,g,即得答案為eE,S,Tw(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])

現在考慮狀壓求f,gf,g,列舉點集SSSS中一點uu,下面要把SS分解為兩個不交的集合T,WT,W,因為涉及到計數,故要有順序,否則會記重,令TT為包含S{u}S-\{u\}點集中最小編號的集合,WWTTSS中的補,列舉TT中一點vv,考慮通過將T,WT,W通過邊uvu\leftrightarrow v合併進行對SS狀態的轉移,首先有轉移 g[S][u]+=g[W][u]g[T][v]num[u][v] g[S][u]+=g[W][u]\cdot g[T][v]\cdot num[u][v] 其中num[u][v]num[u][v]表示uvu\leftrightarrow v邊的數量,主要考慮求f[S][u]f[S][u]uvu\leftrightarrow v邊存在狀態有兩種:

1.uuvv的父親,那麼此時uu的深度不變,有轉移 f[S][u]+=f[W][u]g[T][v]num[u][v] f[S][u]+=f[W][u]\cdot g[T][v]\cdot num[u][v] 2.vvuu的父親,此時uu的深度為vv的深度加一,vv的深度加一之和為f[T][v]f[T][v],每種方案的深度要再加一才是uu在這種方案下的深度,點集TT構成一棵樹的方案數為cnt[T]g[T][v]cnt[T]\cdot g[T][v],故有轉移 f[S][u]+=(f[T][v]+cnt[T]g[T][v])g[W][u]num[u][v] f[S][u]+=(f[T][v]+cnt[T]\cdot g[T][v])\cdot g[W][u]\cdot 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;
}