【NOI.ac#31】MST

题目链接

题目描述

小 D 最近学习了最小生成树的有关知识。为了更好地学习求最小生成树的流程,他造了一张 n 个点的带权无向完全图(即任意两个不同的点之间均有且仅有一条无向边的图),并求出了这个图的最小生成树。

为了简单起见,小 D 造的无向图的边权为 [1,n(n−1)/2][1,n(n−1)/2] 之间的整数,且任意两条边的边权均不一样。

若干天后,小 D 突然发现自己不会求最小生成树了。于是他找出了当时求出的最小生成树,但是原图却怎么也找不到了。于是小 D 想要求出,有多少种可能的原图。但是小 D 连最小生成树都不会求了,自然也不会这个问题。请你帮帮他。

形式化地,你会得到 n−1 个递增的正整数 a1,a2,⋯,an−1,依次表示最小生成树上的边的边权。你要求出,有多少张 n 个点的带权无向完全图,满足:
每条边的边权为 [1,n(n−1)/2][1,n(n−1)/2] 之间的整数;
任意两条不同的边的边权也不同;
至少存在一种最小生成树,满足树上的边权按照从小到大的顺序排列即为 a1,a2,⋯,an−1(事实上,可以证明任意一张无向图的任意两棵最小生成树上的边权集合相同)。

因为答案可能很大,所以你只要求出答案对 109+7(一个质数)取模的结果即可。

Sol

DP

暴力dp的做法:
用最小表示法记录每个点在的联通块 , 然后把这个数组 哈希 或者 map 掉

要加入一条树边时,先要加入之前的非树边 , 而不能改变树的形态 , 因此可以直接暴力算出有多少个不会使得连通性改变的边 排列数计算方案就可以了

加入树边则暴力枚举合并哪两个块然后转移

考虑优化:
其实有一些状态的转移是一样的 , 这样的情况发生在两个状态的无序处理后是相同的情况下

而其实这个就是 一个 n 的无序划分 , 所以考虑直接用整数的拆分数来表示状态 , 即使n是40 ,拆分数也不足 40000

所以暴搜出拆分数,然后 哈希 或 map 掉 , 和上面一样的转移就好了

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstdlib>
#include<set>
#include<map>
#define Set(a,b) memset(a,b,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(a))
using namespace std;
int n;
const int mod=1e9+7;
const int N=41;
const int UP=40000;
int a[N];
int S=0;
const int MO=1e6+3;
const int base=131;
struct fen{
    
	int num[41];
	inline void clear(){
    num[0]=0;}
	inline int size(){
    return num[0];}
	inline void push(int x){
    num[++num[0]]=x;}
	inline void pop(){
    --num[0];}
	inline void trans(){
    
		int st[41];Set(st,0);
		for(register int i=1;i<=num[0];++i) ++st[num[i]];
		Copy(num,st)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值