NOI.AC 31 MST——整数划分相关的图论(生成树、哈希)
模拟 kruscal 的建最小生成树的过程,我们应该把树边一条一条加进去;在加下一条之前先把权值在这一条到下一条的之间的那些边都连上。连的时候要保证图的连通性不变。
已经加了一些树边之后,图的连通性是怎样的呢?这可以是一个整数划分的问题。据说方案只有4万多,所以可以搜一下,搜出有 k 个连通块的方案数。
为了转移和转移时算方案数,还要记录每个方案的:各个连通块的点数,所有的空位(可放边)数。
可以用 map 来存状态。 map 的角标是一个随便哈希的值,map 的值是这个状态的编号,也是这个状态的其他信息在那些数组里的角标。这样要算下一个状态是谁的时候就可以通过记录的“各个连通块的点数”找到”下一个状态的各个连通块的点数“,再用一样的方法哈希起来,利用 map 就能找到下一个状态的编号了。
因为不太会写,所以就学习(抄)了一下别人的。
1.注意算排列时判 n<m !数组越界本地可能答案正确,但交上去就会爆。
2.不知 1e4 是怎么确定的?
3.学题解 N=41 WA了最后两个点,改成45就A了。不知为何。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define ull unsigned long long
#define ll long long
using namespace std;
const int N=,M=1e4,mod=1e9+,base=M;//N=41会WA两个点?!
int n,a[N],cd[N],vec[N][M][N],edg[N][M];//N的情况里第M个的 空位/第N个连通块的点数
int dp[N][M],lm,tmp[N],id[N][N],tmpx[N],top;
int jc[N*N],jcn[N*N];
ull hsh;
map<ull,int> mp[N];//用值得到另一个角标(cd)
int rdn()
{
int ret=;bool fx=; char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=; ch=getchar();}
while(ch>=''&&ch<='') ret=(ret<<)+(ret<<)+ch-'',ch=getchar();
return fx?ret:-ret;
}
int pw(int x,int k)
{
int ret=;while(k){if(k&1ll)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1ll;}return ret;
}
void init()
{
lm=(n*(n-)>>);//not n
jc[]=;
for(int i=;i<=lm;i++) jc[i]=(ll)jc[i-]*i%mod;
jcn[lm]=pw(jc[lm],mod-);
for(int i=lm-;i>=;i--) jcn[i]=(ll)jcn[i+]*(i+)%mod;
}
void dfs(int sm,int cr,int lst)
{
if(lst*(lm-cr+)>sm) return;
if(cr>lm)
{
hsh=;
for(int i=;i<cr;i++)hsh=hsh*base+tmp[i];
mp[lm][hsh]=++cd[lm];
for(int i=;i<cr;i++)
{
vec[lm][cd[lm]][i]=tmp[i];
edg[lm][cd[lm]]+=(tmp[i]*(tmp[i]-)>>);
}
//printf("edg[%d][%d]=%d\n",lm,cd[lm],edg[lm][cd[lm]]);
return;
}
if(cr==lm)
{
tmp[cr]=sm;dfs(,cr+,);//有剪枝,所以一定不降
return;
}
for(int i=lst;i<=sm;i++)
{
tmp[cr]=i;
dfs(sm-i,cr+,i);
}
}
int P(int n,int m)
{
if(n<m) return ;//!!!!!!
//printf("jc[%d]=%d jcn[%d]=%d\n",n,jc[n],n-m,jcn[n-m]);
return (ll)jc[n]*jcn[n-m]%mod;
}
int main()
{
n=rdn();
init();
for(int i=n;i>;i--) a[i]=rdn(); a[]=(n*(n-)>>);
for(int i=;i<=n;i++) lm=i,dfs(n,,);
dp[n][]=;
for(int i=n;i>;i--)
for(int s=;s<=cd[i];s++)
{
//printf("y dp[%d][%d]=%d\n",i,s,dp[i][s]);
//printf("%d-%d=%d %d-%d-1=%d\n",edg[i][s],a[i+1],edg[i][s]-a[i+1],a[i],a[i+1],a[i]-a[i+1]-1);
dp[i][s]=(ll)dp[i][s]*P(edg[i][s]-a[i+],a[i]-a[i+]-)%mod;
//printf("now dp[%d][%d]=%d(P=%d)\n",i,s,dp[i][s],P(edg[i][s]-a[i+1],a[i]-a[i+1]-1));
memset(id,,sizeof id);
for(int j=;j<=i;j++)tmp[j]=vec[i][s][j];
for(int u=;u<=i;u++)
for(int v=u+;v<=i;v++)//哪两个集合
{
//printf("i=%d s=%d tmp[%d]=%d tmp[%d]=%d\n",i,s,u,tmp[u],v,tmp[v]);
if(!id[tmp[u]][tmp[v]])
{
top=;
for(int p=;p<=i;p++)
if(p!=u&&p!=v)tmpx[++top]=tmp[p];
tmpx[++top]=tmp[u]+tmp[v];
for(int p=top-;p;p--)//插排
if(tmpx[p]>tmpx[p+])swap(tmpx[p],tmpx[p+]);
else break;//else
hsh=;
for(int p=;p<=top;p++)
hsh=hsh*base+tmpx[p];
id[tmp[u]][tmp[v]]=mp[i-][hsh];
//printf("id[%d][%d]=%d\n",tmp[u],tmp[v],id[tmp[u]][tmp[v]]);
}
dp[i-][id[tmp[u]][tmp[v]]]=
(dp[i-][id[tmp[u]][tmp[v]]]+(ll)dp[i][s]*tmp[u]*tmp[v])%mod;
//printf("dp[%d][%d]=%d(dp=%d tmu=%d tmv=%d)\n",i-1,id[tmp[u]][tmp[v]],dp[i-1][id[tmp[u]][tmp[v]]],dp[i][s],tmp[u],tmp[v]);
}
}
//printf("%d-%d=%d %d-%d-1=%d\n",edg[1][1],a[2],edg[1][1]-a[2],a[1],a[2],a[1]-a[2]-1);
dp[][]=(ll)dp[][]*P(edg[][]-a[],a[]-a[]-)%mod;
printf("%d\n",dp[][]);
return ;
}
NOI.AC 31 MST——整数划分相关的图论(生成树、哈希)的更多相关文章
- NOI.ac #31 MST DP、哈希
题目传送门:http://noi.ac/problem/31 一道思路好题考虑模拟$Kruskal$的加边方式,然后能够发现非最小生成树边只能在一个已经由边权更小的边连成的连通块中,而树边一定会让两个 ...
- NOI.AC #31 MST —— Kruskal+点集DP
题目:http://noi.ac/problem/31 好题啊! 题意很明白,对于有关最小生成树(MST)的题,一般是要模拟 Kruskal 过程了: 模拟 Kruskal,也就是把给出的 n-1 条 ...
- NOI.AC #31. MST
好像又是神仙dp....gan了一早上 首先这是个计数类问题,上DP, 对于一个最小生成树,按照kruskal是一个个联通块,枚举边小到大合成的 假如当前边是树边,那么转移应该还是枚举两个块然后合并 ...
- [NOI.AC#31]MST 计数类DP
链接 注意到 \(n\) 只有40,爆搜一下发现40的整数拆分(相当于把 \(n\) 分成几个联通块)很少 因此可以枚举联通块状态来转移,这个状态直接用vector存起来,再用map映射,反正40也不 ...
- noi.ac #39 MST
MST 模板题 #include <iostream> #include <cstdio> #include <algorithm> #include <cm ...
- 【noi 2.6_8787】数的划分(DP){附【转】整数划分的解题方法}
题意:问把整数N分成K份的分法数.(与"放苹果"不同,在这题不可以有一份为空,但可以类比)解法:f[i][j]表示把i分成j份的方案数.f[i][j]=f[i-1][j-1](新开 ...
- NOI.AC: NOIP2018 全国模拟赛习题练习
闲谈: 最后一个星期还是不浪了,做一下模拟赛(还是有点小虚) #30.candy 题目: 有一个人想买糖吃,有两家商店A,B,A商店中第i个糖果的愉悦度为Ai,B商店中第i个糖果的愉悦度为Bi 给出n ...
- 整数划分 Integer Partition(一)
话说今天百度面试,可能是由于我表现的不太好,面试官显得有点不耐烦,说话的语气也很具有嘲讽的意思,搞得我有点不爽.Whatever,面试中有问到整数划分问题,回答这个问题过程中被面试官搞的不胜其烦,最后 ...
- BZOJ1263: [SCOI2006]整数划分
1263: [SCOI2006]整数划分 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 677 Solved: 332[Submit][Status] ...
随机推荐
- 深入理解Java 8 Lambda
- 转载:blog1, blog2 以上两篇博客是对lambda表达式的深入理解,用于后续加深理解. 如下先从零开始理解lambda, 1. 接触lambda表达式是从python,javascrip ...
- Getting Started with the G1 Garbage Collector(译)
原文链接:Getting Started with the G1 Garbage Collector 概述 目的 这篇教程包含了G1垃圾收集器使用和它如何与HotSpot JVM配合使用的基本知识.你 ...
- CGI FASTCGI php-fpm
CGI(Common Gateway Interface) CGI全称是“公共网关接口”(Common Gateway Interface),HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工 ...
- 【转载】【selenium+Python WebDriver】之元素定位
总结: 感谢: “煜妃”<Selenuim+Python之元素定位总结及实例说明> “Huilaojia123”<selenium WebDriver定位元素学习总结> “上海 ...
- Android 繪圖白板元件,有畫筆和板擦的功能 (转)
package com.example.drawboard; import java.util.ArrayList; import java.util.List; import android.con ...
- tao.opengl+C#绘制三维模型
一.tao.Opengl技术简介 Opengl是一种C风格的图形库,即opengl中没有类和对象,只有大量的函数.Opengl在内部就是一个状态机,利用不同的函数来修改opengl状态机的状态,以达到 ...
- Windows系统下正确安装MongoDB
1.下载.安装 官网下载: http://www.mongodb.org/downloads 下载好之后,接下来进行安装了: 2.创建数据文件夹 MongoDB将数据文件夹存储在 db 文件夹下. 可 ...
- Continuously Integrate
 Continuously Integrate David Bartlett THE Build AS A "Big BAng" EvEnT in project develop ...
- EasyNVR摄像机H5流媒体服务器在windows上批处理脚本自动以管理员权限运行
很多时候, 我们需要以管理员权限来运行批处理脚本, 比如操作 windows 服务. EasyNVR 中提供安装服务的批处理脚本, 运行这个bat文件, 自动将 EasyNVR 以 windows 服 ...
- 【分享】自己写的一个可空的DateTimePicker控件-附源码
最近这段时间在重构以前的一个项目,其中有一项就是要把DateTimePicker控件值可空.大家都知道的DateTimePicker值为DateTime类型,DateTime类型值不能等于Null.但 ...