【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+ ...
随机推荐
- .net 小程序获取用户UnionID
第一次写博客,写的不好多多海涵! 1.小程序获取UnionID的流程用code去换取session_key,然后去解密小程序获取到的那串字符! 话不多说,原理大家都懂!!!!!! 直接上代码 publ ...
- c# 运算符:? ,??
参考微软帮助 1 ? 空值条件运算符,用于在执行成员访问 (?.) 或索引 (?[) 操作之前,测试是否存在 NULL. // ? 空值条件运算符 string str = null; Conso ...
- php的借用其他网站的页面覆盖Logo的技巧
php的借用其他网站的页面覆盖Logo的技巧, <body> <div id="red_f"></div> <div class=&quo ...
- 关于win10系统1709版本安装JDK出现变量配置正确但仍有“java不是内部或外部命令”的解决办法
背景:联想拯救者R720笔记本,系统一键还原了,需要重新安装一部分软件,最基本的就是JDK,但今天在安装时遇到了问题,之前安装的1.8版本,没有仔细配置环境变量,这一次安装的是1.7版本的,仔仔细细配 ...
- 【JavaScript中typeof、toString、instanceof、constructor与in】
JavaScript中typeof.toString.instanceof.constructor与in JavaScript 是一种弱类型或者说动态语言.这意味着你不用提前声明变量的类型,在程序运行 ...
- Docker学习笔记 - 在运行中的容器内启动新进程
docker psdoker top dc1 # 容器情况# 在运行中的容器内启动新进程docker exec [-d] [-i] [-t] 容器名 [command] [args]docker ex ...
- python isinstance 函数
isinstance是Python中的一个内建函数 语法: isinstance(object, classinfo) 如果参数object是classinfo的实例,或者object是class ...
- awk、变量、运算符、if多分支
awk.变量.运算符.if多分支 awk: 语法 awk [options] 'commands' files option -F 定义字段分隔符,默认的分隔符是连续的空格或制表符 使用option中 ...
- AtCoder Beginner Contest 073
D - joisino's travel Time Limit: 2 sec / Memory Limit: 256 MB Score : 400400 points Problem Statemen ...
- Hive函数:SUM,AVG,MIN,MAX
转自:http://lxw1234.com/archives/2015/04/176.htm,Hive分析窗口函数(一) SUM,AVG,MIN,MAX 之前看到大数据田地有关于max()over(p ...