Newcoder 147 B.Enumeration not optimization(状压DP+树形DP)

58 篇文章 1 订阅
40 篇文章 1 订阅

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}wemax(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) (1n12,1m1000,1x,yn,1z5000)

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]) eE,S,Tw(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 uv合并进行对 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 uv边的数量,主要考虑求 f [ S ] [ u ] f[S][u] f[S][u] u ↔ v u\leftrightarrow v uv边存在状态有两种:

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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值