题目:http://noi.ac/problem/31

好题啊!

题意很明白,对于有关最小生成树(MST)的题,一般是要模拟 Kruskal 过程了;

模拟 Kruskal,也就是把给出的 n-1 条边一条一条加进去,那么就要枚举每次连接了哪两个连通块(点集);

于是需要记录连通块情况,这样加一条边就相当于一种情况到另一种情况的转移,就可以DP;

记录连通块情况较为复杂,而且还要注意不重复等等...

但实际上,我们在转移时,并不需要知道连通块中有哪些点,只要知道连通块的大小即可(从n个1开始转移时已有所区分);

所以可以通过 dfs 求出所有连通块情况(枚举连通块个数及大小),把它们哈希记录下来;

然后就可以转移,从 i+1 个点集转移到 i 个点集,需要枚举是哪两个点集合并了;

答案的增加体现在合并点集时连的 MST 边有点数×点数种方案,而且一个点集状态中不是 MST 边的边可以分配权值;

代码挺复杂而精美...模仿了一篇AC代码,为了看懂加了许多注释,都挪过来了。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int const maxn=,maxm=,mod=1e9+,base=;//
int n,s,a[maxn],vec[maxn][maxm][maxn],len[maxn],edg[maxn][maxm],tmp[maxn];
int inv[maxn*maxn],jc[maxn*maxn],jcn[maxn*maxn],f[maxn][maxm],id[maxn][maxn];
map<ull,int>mp[maxn];
void init()
{
inv[]=jc[]=jcn[]=;
for(int i=;i<maxn*maxn;i++)inv[i]=((ll)(mod-mod/i)*inv[mod%i])%mod;//
for(int i=;i<maxn*maxn;i++)jc[i]=((ll)jc[i-]*i)%mod,jcn[i]=((ll)jcn[i-]*inv[i])%mod;//
}
void dfs(int nw,int i,int lst)//剩余点数,已有点集数,上一个点集的点数
{
if((s-i+)*lst>nw)return;//剩余点集数*上一个点集点数(剩余点数总和的极小值)>剩余点数总和
if(i==s+)
{
len[s]++; ull hsh=;//hsh 不是 int,不能 mod!
for(int j=;j<=s;j++)
{
vec[s][len[s]][j]=tmp[j];//s个点集的情况中第len[s]种状态的各个点集
edg[s][len[s]]+=((tmp[j]*(tmp[j]-))>>);//C(tmp[j],2)
hsh=hsh*base+tmp[j];
}
mp[s][hsh]=len[s]; return;//s中的这种状态的哈希值对应编号
}
else if(i==s)tmp[i]=nw,dfs(,i+,nw);
else for(int j=lst;j<=nw;j++)tmp[i]=j,dfs(nw-j,i+,j);//=
}
ll P(int n,int m)// n!/(n-m)!
{
if(n<m)return ;
return (ll)jc[n]*jcn[n-m]%mod;
}
int main()
{
scanf("%d",&n);
for(int i=;i<n;i++)scanf("%d",&a[i]); reverse(a+,a+n);//从大到小
init();
for(s=;s<=n;s++)dfs(n,,);//选点集方案,点数总和为n
f[n][]=;//
for(int i=n;i>;i--)//i个点集 //i>1
for(int j=;j<=len[i];j++)
{
f[i][j]=((ll)f[i][j]*P(edg[i][j]-a[i],a[i-]-a[i]-))%mod;//分配权值 //a[i-1]>a[i]
//(各点集内部的)总边数-a[i] 中选 a[i-1]~a[i] 权值的边的方案(前n-i小的边用于点集内部形成MST的前n-i条边,剩余i个点集)
//有a[i]条边在之前被连,否则轮不到这条MST边来改变连通性
//若该状态不存在,则P(n,m)中n<m,值为0 //n>m 多余空位可在最后补
memset(id,,sizeof id);
for(int k=;k<=i;k++)tmp[k]=vec[i][j][k];
for(int k=;k<=i;k++)
for(int l=k+;l<=i;l++)
{
if(!id[tmp[k]][tmp[l]])//(记忆化) //k,l各不相同但tmp[k],tmp[l]可能相同
{
ull hsh=; bool flag=;
for(int p=;p<=i;p++)//tmp[k,l,p]都以点数代表点集
{
if(p==k||p==l)continue;
if(!flag&&tmp[p]>tmp[k]+tmp[l])flag=,hsh=hsh*base+tmp[k]+tmp[l];
//第一个p>k+l(点数,保证从小到大哈希),把k,l作为一个连通块哈希
hsh=hsh*base+tmp[p];
}
if(!flag)hsh=hsh*base+tmp[k]+tmp[l];//!
id[tmp[k]][tmp[l]]=mp[i-][hsh];
}
int d=id[tmp[k]][tmp[l]];//此哈希值对应的选点集状态编号(k,l作为一个连通块)
f[i-][d]=(f[i-][d]+(ll)f[i][j]*tmp[k]*tmp[l])%mod;//连接点集的边是MST边,有点数*点数种连边方案
}
}
// f[1][1]=((ll)f[1][1]*jc[a[1]-1])%mod;
f[][]=((ll)f[][]*jc[edg[][]-a[]])%mod;//剩余空位
printf("%d\n",f[][]);
return ;
}

NOI.AC #31 MST —— Kruskal+点集DP的更多相关文章

  1. [NOI.AC#31]MST 计数类DP

    链接 注意到 \(n\) 只有40,爆搜一下发现40的整数拆分(相当于把 \(n\) 分成几个联通块)很少 因此可以枚举联通块状态来转移,这个状态直接用vector存起来,再用map映射,反正40也不 ...

  2. NOI.ac #31 MST DP、哈希

    题目传送门:http://noi.ac/problem/31 一道思路好题考虑模拟$Kruskal$的加边方式,然后能够发现非最小生成树边只能在一个已经由边权更小的边连成的连通块中,而树边一定会让两个 ...

  3. NOI.AC 31 MST——整数划分相关的图论(生成树、哈希)

    题目:http://noi.ac/problem/31 模拟 kruscal 的建最小生成树的过程,我们应该把树边一条一条加进去:在加下一条之前先把权值在这一条到下一条的之间的那些边都连上.连的时候要 ...

  4. NOI.AC #31. MST

    好像又是神仙dp....gan了一早上 首先这是个计数类问题,上DP, 对于一个最小生成树,按照kruskal是一个个联通块,枚举边小到大合成的 假如当前边是树边,那么转移应该还是枚举两个块然后合并 ...

  5. noi.ac #39 MST

    MST 模板题 #include <iostream> #include <cstdio> #include <algorithm> #include <cm ...

  6. [NOI.AC 2018NOIP模拟赛 第三场 ] 染色 解题报告 (DP)

    题目链接:http://noi.ac/contest/12/problem/37 题目: 小W收到了一张纸带,纸带上有 n个位置.现在他想把这个纸带染色,他一共有 m 种颜色,每个位置都可以染任意颜色 ...

  7. NOI.AC#2139-选择【斜率优化dp,树状数组】

    正题 题目链接:http://noi.ac/problem/2139 题目大意 给出\(n\)个数字的序列\(a_i\).然后选出一个不降子序列最大化子序列的\(a_i\)和减去没有任何一个数被选中的 ...

  8. NOI.AC NOIP模拟赛 第六场 游记

    NOI.AC NOIP模拟赛 第六场 游记 queen 题目大意: 在一个\(n\times n(n\le10^5)\)的棋盘上,放有\(m(m\le10^5)\)个皇后,其中每一个皇后都可以向上.下 ...

  9. NOI.AC WC模拟赛

    4C(容斥) http://noi.ac/contest/56/problem/25 同时交换一行或一列对答案显然没有影响,于是将行列均从大到小排序,每次处理限制相同的一段行列(呈一个L形). 问题变 ...

随机推荐

  1. web前端开发——css

    一.css介绍 1.css是什么? Cascading Style Sheets缩写,层叠样式表.样式定义如何显示HTML元素,样式通常又会存在于样式表中. 2.为什么需要css? 使HTML页面变得 ...

  2. 全文搜索(A)-相关性

    文章:搜索相关性 文章:推荐系统中相似度算法介绍及效果测试 文章:常用的相似度计算方法原理及实现 文章:推荐系统用户相似度计算方法研究

  3. MTK平台如何定位显示花屏和界面错乱等绘制异常的问题?

    [DESCRIPTION] 在测试手机各项功能过程中,经常会遇到概率性复现“屏幕画花了,界面画错乱了等绘制异常问题”,而且概率还非常小: 这类问题请不要直接提交eService,而是先请测试人员及工程 ...

  4. Thinkphp5.0 的视图view的比较标签

    Thinkphp5.0 的视图view的比较标签 {eq name="a" value="10"} <p>相等</p> {else/} ...

  5. [bzoj3306]树_dfs序_线段树_倍增lca

    树 bzoj-3306 题目大意:给定一颗n个节点的树,支持换根.修改点权.查询子树最小值. 注释:$1\le n,q\le 10^5$. 想法: 如果没有换根操作,就是$dfs$序+线段树维护区间最 ...

  6. ZOJ3953 ZJU2017校赛(贪心)

    题意:给出n个区间,求至少删掉多少个区间使得不存在区间a, b, c 两两相交    (定义两个区间相交是,区间[l1, r1]和区间[l2, r2]相交,当且仅当存在一个数x,l1<=x< ...

  7. SQL Server转sqlite数据库

    下载地址:http://files.cnblogs.com/jason-davis/SQL_Server_To_SQLite_DB_Converter_bin.zip 下载地址:http://file ...

  8. 关于SQL命令中不等号(!=,<>)

    比较两个表达式(比较运算符).当比较非空表达式时,如果左边操作数的数值不等于右边的操作数,则结果为 TRUE:否则结果为 FALSE.如果两个操作数中有一个或者两个都为 NULL,并且 SET ANS ...

  9. 纯css3实现按钮的 hover 和 active 时颜色的明暗变化效果

    效果:在任意HTML标签上增加样式类 f-color-control 就可以为此元素增加hover和avtive时颜色的变化; 代码如下: <!DOCTYPE html> <html ...

  10. 传智播客ADO.Net项目开发教程具体解释

    内容简单介绍: 本教程为传智播客.Net培训课堂的现场录像,请到140623ls" target="_blank">传智播客.Net学院下载很多其它免费.Net视频 ...