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

本文介绍了一种算法,用于计算给定无向图的所有有根生成树权值之和,并提供了详细的解决方案及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给出一个nnn个点mmm条边的无向图,边有边权,对于这张图的任意一个有根生成树,定义其权值为
∑e∈{x,y}we⋅max(dx,dy) \sum\limits_{e\in \{x,y\}}w_e\cdot max(d_x,d_y) e{x,y}wemax(dx,dy)
其中dxd_xdxxxx点在生成树中的深度,统计这张图所有有根生成树权值之和,结果模109+710^9+7109+7

Input

第一行输入两个整数n,mn,mn,m表示点数和边数,之后mmm行每行三个整数x,y,zx,y,zx,y,z表示x,yx,yx,y之间有一条权值为xxx的边

(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

输出这张图所有有根生成树权值之和,结果模109+710^9+7109+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)对答案的贡献,贡献分为两部分,uuu在生成树中是vvv的父亲节点以及vvvuuu的父亲节点,以uuuvvv的父亲节点为例,断掉这条边将树分成两个连通块,记vvv所在连通块为TTTuuu所在连通块为SSS,此时需要求出以vvv为根的子树方案数g[T][v]g[T][v]g[T][v],而vvv节点的深度和实际上取决于uuuSSS点集中的深度,以f[S][u]f[S][u]f[S][u]表示uuuSSS状态的所有子树中的深度加一的和,那么我们只要求出f,gf,gf,g,即得答案为∑e∈E,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])eE,S,Tw(f[S][u]g[T][v]+g[S][u]f[T][v])

现在考虑状压求f,gf,gf,g,枚举点集SSSSSS中一点uuu,下面要把SSS分解为两个不交的集合T,WT,WT,W,因为涉及到计数,故要有顺序,否则会记重,令TTT为包含S−{u}S-\{u\}S{u}点集中最小编号的集合,WWWTTTSSS中的补,枚举TTT中一点vvv,考虑通过将T,WT,WT,W通过边u↔vu\leftrightarrow vuv合并进行对SSS状态的转移,首先有转移
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] g[S][u]+=g[W][u]g[T][v]num[u][v]
其中num[u][v]num[u][v]num[u][v]表示u↔vu\leftrightarrow vuv边的数量,主要考虑求f[S][u]f[S][u]f[S][u]u↔vu\leftrightarrow vuv边存在状态有两种:

1.uuuvvv的父亲,那么此时uuu的深度不变,有转移
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] f[S][u]+=f[W][u]g[T][v]num[u][v]
2.vvvuuu的父亲,此时uuu的深度为vvv的深度加一,vvv的深度加一之和为f[T][v]f[T][v]f[T][v],每种方案的深度要再加一才是uuu在这种方案下的深度,点集TTT构成一棵树的方案数为cnt[T]⋅g[T][v]cnt[T]\cdot g[T][v]cnt[T]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] 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;
}

<think>好的,用户想了解Java中Enumeration接口的用法和相关信息。首先,我需要回忆一下Enumeration的基本概念。记得它是Java早期版本中用于遍历集合的接口,后来被Iterator取代,但在一些旧的类如Vector和Hashtable中仍然存在。 接下来,我应该先介绍Enumeration是什么,它的作用以及主要方法。需要提到hasMoreElements()和nextElement()这两个核心方法,并说明它们的使用方式。然后,可能需要对比一下Enumeration和Iterator的区别,比如是否支持移除操作,以及线程安全方面的考虑。 用户可能需要示例代码,所以得准备一个使用Enumeration遍历Vector的例子。代码要简洁明了,展示如何获取Enumeration实例,并用循环遍历元素。同时,需要指出现在推荐使用Iterator,但在旧代码或特定情况下可能还会遇到Enumeration。 另外,参考引用中提到过访问器和修改器方法,虽然不直接相关,但可以提到Enumeration属于访问数据的一种方式,符合封装原则。不过这可能不太相关,需要谨慎引用。 还要注意用户的问题可能涉及使用场景,比如为什么在某些遗留代码中仍然使用Enumeration,或者与现代迭代器的对比。需要解释Enumeration的局限性,比如功能较少,但可能更轻量。 最后,生成相关问题时要覆盖Enumeration的方法、与现代替代品的对比、适用场景以及如何转换到Iterator等。确保问题有针对性,帮助用户深入理解。 检查是否有遗漏点,比如线程安全或者性能方面的考虑,但根据已有知识,Enumeration本身并不特别涉及线程安全,更多是集合类本身的实现。确认示例代码正确,语法无误,符合Java标准写法。 总结回答结构:定义、方法、示例、与现代迭代器的对比、使用场景、相关问题。确保引用正确,比如引用[1]提到XNameAccess接口,可能与集合访问有关,可以适当关联,但需准确。</think>### Java Enumeration接口详解 Java的`Enumeration`接口是Java 1.0引入的集合遍历工具,主要用于访问传统集合类(如`Vector`、`Hashtable`)中的元素。它提供了一种统一的方式来遍历集合,但自Java 1.2起被更强大的`Iterator`接口取代[^1]。 #### 核心方法 1. **`boolean hasMoreElements()`** 检查集合是否还有未遍历的元素。 2. **`E nextElement()`** 返回下一个元素,若没有元素则抛出`NoSuchElementException`。 #### 使用示例 ```java import java.util.Enumeration; import java.util.Vector; public class EnumerationExample { public static void main(String[] args) { Vector<String> colors = new Vector<>(); colors.add("Red"); colors.add("Green"); colors.add("Blue"); Enumeration<String> e = colors.elements(); while (e.hasMoreElements()) { System.out.println(e.nextElement()); } } } ``` 输出: ``` Red Green Blue ``` #### 与`Iterator`的对比 | 特性 | Enumeration | Iterator | |--------------------|----------------------|----------------------| | 移除元素 | 不支持 | 支持`remove()` | | 遍历方向 | 单向 | 单向 | | 线程安全 | 依赖集合实现 | 依赖集合实现 | | 适用场景 | 旧版集合类 | 现代集合框架 | #### 使用场景 1. 需要兼容遗留代码(如使用`Vector`的库)。 2. 仅需简单遍历,无需修改集合内容。 #### 转换为现代迭代器 通过`Collections.list()`可将`Enumeration`转为`List`后使用`Iterator`: ```java Enumeration<String> e = ...; Iterator<String> it = Collections.list(e).iterator(); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值