【Codeforces Round #431 (Div. 1) D.Shake It!】
·最小割和组合数放在了一起,产生了这道题目。
英文题,述大意;
一张初始化为仅有一个起点0,一个终点1和一条边的图。输入n,m表示n次操作(1<=n,m<=50),每次操作是任选一条已存在的边,新建一个编号为(n+1)的节点并向这条边的两个端点连边(共连接两条边)。输入n次操作后满足最小割为m的图有多少种。此处两个图相同当且仅当两边集和点集满足双射。答案取模109+7。
分析:
本题要求方案数。首先读题较困难之处是什么样的两个图算不同的方案。知道每次加入的点是有编号的,有编号的点是独一无二的。在这里画一些图以便清晰理解,以下两个图的情况均为两种方案而不是一种:
回到题目要求,最苛刻的无非是加入了最小割的限制。因此先试放宽要求,不考虑最小割的限制。那么,假如当前进行了n次操作,那么图中的边数可以算出来等于(1+2*n),那么第(n+1)次操作所添加的点共有(1+2*n)种添加方式。没有最小割,这个思路将非常棒。
但是你可以发现,如果添上最小割,上述方法就不可行了。分析失败的原因:每次添加一个点到随意地一个位置的方法难以维护当前图的最小割。因此,我们需要寻找一种方法,既可以通过组合计算方案数(因为这道题不可能枚举每种图,所以计算组合方案是必要的)又可以保证能够在保持最小割为某个值的情况下进行方案数运算。
对于上述需求,我们可以想到,为了能够保持某些性质(最小割),可以通过枚举子问题来实现。所谓子问题就是将原来的问题化成几个规模较小的但是形式和性质相同的问题加以解决(如动态规划利用了子问题的性质)。
本题的图怎样拆成子问题呢?入手点可以选择为最早的点数为3的图。我们发现,这个点数为3的图可以由这些部分组成:
这里面已经有了子问题的影子。观察任意一条新添边和其两端点组成的子图,其实就是操作数为0的时候的图(即只有起点终点的图),也就是说,我们正尝试把当前图拆为一条主干边及两端点和一些操作数较小的子图。我们来欣赏这个样例吧:
为什么偏要分成两部分?原因是可以快速维护最小割。该图的最小割就等于min{子问题1图的最小割,子问题2图的最小割}。我们可以将这样一对子问题看成在主干边上建一座大桥。我们知道主干边上是可以建很多座大桥的:
图为三座大桥。当多座桥存在的时候,最小割的计算方式为每座桥的最小割相加。得到这个结论后可以发现,一个图由主干边加很多桥组成,由于图的点数大于这些桥的点数,因此从点数由小到大的递推状态转移具有可行性。
想想递推求方案数。令:
f[i][j]表示当前进行了i次操作(即有(i+2)个点),图的最小割为j的种类数
g[i][j]表示当前进行了i次操作,最小割为j的大桥的种类数
这个状态看上去g[i][j]是专门为f[i][j]服务的。那好吧就先这样认为吧,因为上文讲的内容全是用各种各样的桥构造当前图。先别急桥怎么处理,不过要记住一点:一个桥的本质是一个子问题二元组(见上文桥的来源就知道了)。
首先想一个填表式的伪状态转移方程:
f[这个大大的图]+=f[这个大大地图-某座桥]*g[这桥]
画图可以表示为:
接下来我们一步步细化方程式。设图的操作数为i,最小割为j。
[第一步]将上式子转化为较为正统的方程式:
设枚举的桥操作数为i,最小割为j。
f[x][y]+=f[x-i][x-j]*g[i][j]
[第二步]由于同种桥可以添加多次,所以完善方程式:
设当前操作数为i,最小割为j的桥将被使用T次。
f[x][y]+=f[x-i*T][y-j*T]*MultiCombination(g[i][j],T)
我们知道操作数为i,最小割为j的桥共有g[i][j]种,此处我们要从中选出T座桥,由于相同桥可以多次选择,因此问题转化为在g[i][j]中选出T个桥,一个桥可以被多次选择的方案数,即MultiCombination,其值等于C(g[i][j]+T-1,T)
[第三步]为了做到能够边转移边递推多重组合(MC),将方程转为刷表法:
设当前操作数为i,最小割为j的桥将被使用T次。
f[x+i*T][y+j*T]+=f[i][j]*MultiCombination(g[i][j],T)
转化形式的好处就是我们可以顺次枚举T,那么可以使用一个comb来表示当前C(g[i][j]+T-1,T)的值,若T变为(T+1),那么可以直接递推:
comb=comb*(g[i][j]+T)/T
[合法性证明]刷表法到达i,j的时候,由于小于i,j的子问题已经贡献了答案,因此此时f[i][j]已经是正确方案数,用它来更新更大的状态不会造成遗漏。
至此,我们已经成功地借助g[][]完成了对f[][]的递推。该想想g[i][j]怎么求了。我们回到最初,看看桥是什么东西——桥指的是两个满足条件的图A,B并且对于当前的大图,其中一个图和这个大图起点重合,另一个图和这个大图终点重合,然后第一个图的终点和第二个图的起点重合:(回顾一下吧)
然后就很奇妙:我们发现两个子问题图的操作数一定小于大图的操作数,也就是说满足从大到小的关系——小图的f[][]不已经求出来了吗?因此桥是需要由组成它的两个子图来计算方案数的。在本文开始的时候提到过怎么样的两个图算是不同,这里我们计算g[i][j]的方法是,先固定左边的子问题图的最小割等于j,然后右边的图最小割随意为多少,并且保证两个图的操作数加起来等于i-1(-1的原因画图可知),然后左右反过来进行一次:
g[i][j]+=f[a][j]*f[i-1-b][u] (u>=j)
g[i][j]+=f[a][u]*f[i-1-b][j] (u> j)
总结来说,本题的核心在于寻找子问题从而求解方案数,同时结合了本题的加边特点维护最小割。代码在这里:
#include<stdio.h>
#include<cstring>
#define ll long long
#define M 1000000007
#define go(i,a,b) for(int i=a;i<=b;i++)
const int N=;int n,m;
ll f[N][N],g[N][N],t[N][N],INV[N];
int main()
{
scanf("%d%d",&n,&m);f[][]=;
INV[]=;go(i,,)INV[i]=(-M/i*INV[M%i])%M; go(i,,n)go(j,,i+)
{
go(a,,i-){int b=i--a;
go(u, j ,a+)(g[i][j]+=f[a][u]*f[b][j])%=M;
go(u,j+,b+)(g[i][j]+=f[a][j]*f[b][u])%=M;} memset(t,,sizeof(t)); go(x,,n)go(y,,x+){ll comb=;
go(T,,n){if(x+T*i>n)break;
(comb*=(g[i][j]+T-)%M*INV[T]%M)%=M;
(t[x+i*T][y+j*T]+=comb*f[x][y]%M)%=M;}} go(x,,n)go(y,,x+)(f[x][y]+=t[x][y])%=M; }
printf("%d",(f[n][m]%M+M)%M);return ;
}//Paul_Guderian
A never-ending, fast-changing and dream-like world unfolds,
as the secret door opens…… ——————本题题干节选
【Codeforces Round #431 (Div. 1) D.Shake It!】的更多相关文章
- 【Codeforces Round 431 (Div. 2) A B C D E五个题】
先给出比赛地址啦,感觉这场比赛思维考察非常灵活而美妙. A. Odds and Ends ·述大意: 输入n(n<=100)表示长度为n的序列,接下来输入这个序列.询问是否可以将序列划 ...
- 【Codeforces Round #431 (Div. 2) A】Odds and Ends
[链接]点击打开链接 [题意] 让你把一个数组分成奇数个部分. 且每个部分的长度都是奇数. [题解] 很简单的脑洞题. 开头和结尾一定要为奇数,然后 n为奇数的话,就选整个数组咯. n为偶数的话,不能 ...
- 【Codeforces Round #431 (Div. 2) B】 Tell Your World
[链接]点击打开链接 [题意] n个点,x从左到右严格递增的顺序给出 让你划两条平行的,且没有相同点的直线; 使得它们俩各自最少穿过一个点. 且它们俩穿过了所有的点. [题解] 枚举第一个点和哪个点组 ...
- 【Codeforces Round #431 (Div. 2) C】From Y to Y
[链接]点击打开链接 [题意] 让你构造一个大小最多为10W的字符multiset. 你进行n-1次操作; 每次操作,从set中取出两个字符串,一开始单个字符被认为是字符串. 然后把它们连接在一起. ...
- 【Codeforces Round #431 (Div. 1) B】
[链接]h在这里写链接 [题意] 场上有 n 个点,它们分别向上与向右在不同时刻开始运动,相遇则改变移动方向,求最终这些点到达的坐标. [题解] 先把每个点的坐标都往它本该移动的方向相反的方向退ti个 ...
- 【Codeforces Round #476 (Div. 2) [Thanks, Telegram!] C】Greedy Arkady
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 枚举那个人收到了几次糖i. 最好的情况显然是其他人都只收到i-1次糖. 然后这个人刚好多收了一次糖 也即 (i-1)kx + x & ...
- 【Codeforces Round #476 (Div. 2) [Thanks, Telegram!] E】Short Code
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 先建立一棵字典树. 显然,某一些节点上会被打上标记. 问题就转化成求所有标记的深度的和的最小值了. (标记可以上移,但是不能在同一位 ...
- 【Codeforces Round #476 (Div. 2) [Thanks, Telegram!] D】Single-use Stones
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 设长度为L的所有区间里面,石头的个数的最小值为k 设取到k的区间为l,r 那么k就为最多能通过的青蛙个数. 假设k再大一点.比如为k ...
- 【Codeforces Round #476 (Div. 2) [Thanks, Telegram!] A】Paper Airplanes
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 统计每个人需要的sheet个数. 乘上k 然后除p就是需要的pack个数了 [代码] #include <bits/stdc+ ...
随机推荐
- 去掉xcode编译warning:ld: warning: directory not found for option '-L
选择工程, 编译的 (targets) 选择 Build Settings 菜单 查找 Library Search Paths 和 Framework Search Paths, 删掉编译报warn ...
- LR回放https协议脚本失败: 错误 -27778: 在尝试与主机“www.baidu.com”connect 时发生 SSL 协议错误
今天用LR录制脚本协议为https协议,回放脚本时出现报错: Action.c(14): 错误 -27778: 在尝试与主机"www.baidu.com"connect 时发生 S ...
- zookeeper入门系列:paxos协议
上一章讨论了一种强一致性的情况,即需要分布式事务来解决,本章我们来讨论一种最终一致的算法,paxos算法. paxos算法是由大牛lamport发明的,关于paxos算法有很多趣事.比如lamport ...
- EasyUI中easyui-combobox的onchange事件。
html: <select id="cbox" class="easyui-combobox" name="dept" style=& ...
- MySQL ID排序乱了的解决办法
可能在整理表中数据的时候删除了某一行数据,导致ID空缺,下面是我用到的解决办法:(请先备份,MySQL备份方法见 MySQL->MySQL备份) 使用ALTER DROP删除原有的ID字段: A ...
- Java看书学习笔记
1.POM:maven ,项目管理工具存放Jar包的文件2.mybatis-generator-core-1.3.2 生成文件 生成语句: java -jar mybatis-generator-co ...
- Mego(03) - ORM框架的新选择
前言 从之前的两遍文章可以看出ORM的现状. Mego(01) - NET中主流ORM框架性能对比 Mego(02) - NET主流ORM框架分析 首先我们先谈下一个我们希望的ORM框架是什么样子的: ...
- C#-获取字符的GBK编码值
public static int GetGBKValue(string key) { byte[] gbk = Encoding.GetEncoding("GBK").GetBy ...
- ( 转 ) CORS 有一次 OPTIONS 请求的原理
刚接触前端的时候,以为HTTP的Request Method只有GET与POST两种,后来才了解到,原来还有HEAD.PUT.DELETE.OPTIONS-- 目前的工作中,HEAD.PUT.DELE ...
- 多台linux主机之间建立免密通信
多台linux主机之间设置免密通信 例:A 向 B设置免密通信 第一步 A生成密钥对 ssh-keygen -t rsa -t 指定秘钥的类型 rsa 秘钥类型 密钥目录:/root/.ssh/(如果 ...