CF979E
非常好的dp,非常考dp的能力
很显然是个计数问题,那么很显然要么是排列组合,要么是递推,这道题很显然递推的面更大一些。
那么我们来设计一下状态:
设状态f[i][j][k][p]表示目前到了第i个点,这i个点中有j个白点是奇数条好的路径的结尾,k个黑点是奇数条好的路径的结尾,p个白点是偶数条好的路径的结尾的方案数
可能这个状态本身不是特别好懂,我们详细解释一下:
这样的图的个数会取决于好的路径的条数,而好的路径的条数又可以分成两类:以黑点为结尾和以白点为结尾
那么对于每一个黑点或白点,他只有两种可能:有奇数条好的路径在这结尾和有偶数条好的路径在这结尾。
这样我们就将整个图的点分为了4类:白点是奇数条好的路径的结尾,黑点是奇数条好的路径的结尾,白点是偶数条好的路径的结尾,黑点是偶数条好的路径的结尾。
我们将其中三种扔进状态里,而第四个就可以算出来。
接着我们考虑转移:
分类转移:如果一个节点是白色,那么一个合法的路径一定会从上一个黑点转过来。
而且如果想要影响这个白点奇偶性,上面一定会连上一个奇数黑点,因为偶数黑点对这个白点的奇偶性没有影响。
我们讨论这个白点是奇数白点还是偶数白点:如果这个白点是奇数白点,那么从上面的奇数黑点转过来时,要连1个,3个...奇数个,这样才能保证这个白点是奇数的。
那么如果设之前奇数黑点的数量为k,总方案数即为
利用这一点,乘原来方案数转移即可
那么这个白点是偶数白点的转移也同理,总方案数即为
同理转移即可
最后,考虑其他点:由于别的点对这个白点的奇偶性没有影响,所以对于别的点可以随便连,也就是2^别的点的个数,乘这个东西即可
那么黑点也就同理了。
贴代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
#define mode 1000000007
using namespace std;
ll dp[55][55][55][55];//到了第几个点,奇数白点,奇数黑点,偶数白点
int n,typ;
int c[55];
ll mul[55];
int main()
{
scanf("%d%d",&n,&typ);
mul[0]=1;
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);//0黑1白-1任意
mul[i]=(mul[i-1]<<1)%mode;
}
ll ret=0;
if(c[1]==0||c[1]==-1)
{
dp[1][0][1][0]=1;
if(typ==1&&n==1)
{
ret++;
}
}
if(c[1]==1||c[1]==-1)
{
dp[1][1][0][0]=1;
if(typ==1&&n==1)
{
ret++;
}
}
for(int i=2;i<=n;i++)
{
for(int j=0;j<=i;j++)//奇数白
{
for(int k=0;k+j<=i;k++)//奇数黑
{
for(int p=0;j+k+p<=i;p++)//偶数白
{
int t=i-j-k-p;//偶数黑
if(c[i]==1||c[i]==-1)
{
ll s=0;
if(j+p)
{
if(j)
{
if(!k)
{
s+=dp[i-1][j-1][k][p];
}else
{
s+=mul[k-1]*dp[i-1][j-1][k][p]%mode;
}
s%=mode;
}
if(p)
{
if(k)
{
s+=mul[k-1]*dp[i-1][j][k][p-1]%mode;
}
s%=mode;
}
}
s*=mul[j+p+t-1];
s%=mode;
dp[i][j][k][p]+=s;
dp[i][j][k][p]%=mode;
}
if(c[i]==0||c[i]==-1)
{
ll s=0;
if(k+t)
{
if(k)
{
if(!j)
{
s+=dp[i-1][j][k-1][p];
}else
{
s+=mul[j-1]*dp[i-1][j][k-1][p]%mode;
}
s%=mode;
}
if(t)
{
if(j)
{
s+=mul[j-1]*dp[i-1][j][k][p]%mode;
}
s%=mode;
}
}
s*=mul[k+p+t-1];
s%=mode;
dp[i][j][k][p]+=s;
dp[i][j][k][p]%=mode;
}
if(i==n)
{
if((j+k)%2==typ)
{
ret+=dp[i][j][k][p];
ret%=mode;
}
}
}
}
}
}
printf("%lld\n",ret);
return 0;
}
CF979E的更多相关文章
- ZR8.2 DP
DP 1CF1101D 我们发现,最终答案一定和质因数有关 我们发现\(w_i <= 2*10^5\)级别的树,他的素因子的个数不会非常多(\(<=10\)) 然后我们就设 gcd是\(d ...
随机推荐
- Linux查看系统的基本信息
uname -r :显示操作系统的发行版号 uname -a:显示系统名.节点名称.操作系统的发行版号.操作系统版本.运行系统的机器 ID 号. # Ubuntu系统 ubuntu@VM-28-69- ...
- 第五节,损失函数:MSE和交叉熵
损失函数用于描述模型预测值与真实值的差距大小,一般有两种比较常见的算法——均值平方差(MSE)和交叉熵. 1.均值平方差(MSE):指参数估计值与参数真实值之差平方的期望值. 在神经网络计算时,预测值 ...
- Tensorflow的Queue读取数据机制
参考链接:http://www.sohu.com/a/148245200_115128
- ajax-hook
// ==UserScript== // @name ajax hook 调试 // @namespace http://tampermonkey.net/ // @version 0.1 // @d ...
- page.isvalid
背景 看到这个标题,想了半天,为啥用的.应该是当初前台要动态增加行这个事情,当初用.net真是用傻了,竟然对html.aspx原理不大清楚,对于html也想着后台生成.真是弱智啊.谈到这里,想到c#这 ...
- cryptsetup文件系统加密
今天做了SYC攻防题的文件系统挂载部分,在找到挂载最内层的final文件时发现mount无法识别,这也许就是一个加密的文件系统吧,还好-在龟速的 网络环境下查阅到了losetup循环挂载系统命令,但是 ...
- 利用HBuilder将vue项目打包成移动端app
事先准备,开发完成的web app项目(也可以利用vue-cli脚手架构建vue模板项目),npm run dev可以正常预览的项目 1,将项目目录下config文件内index.js中assetsP ...
- Vue 实战项目开发流程
一 基础配备 ## 一.环境搭建 #### 1.安装node - 去[官网](https://nodejs.org/zh-cn/)下载node安装包 - 傻瓜式安装 - 万一安装后终端没有node环境 ...
- 查询每个分组中第N的一条记录
查询每个分组中第N的一条记录 -- 天气表,每天每个地区采集了多条记录的天气信息,但是时间只记录到了天,导致同一个地区同一天出现了多条天气记录 -- 目的:获取所有地区在每天中第N的一条记录 sele ...
- Linux中给普通用户添加sudo权限
使用Linux系统时,经常会被要求使用超级权限,但是root的权限太过大了,一般慎用!!!因此可以通过给普通用户添加sudo权限,平常用普通用户进行操作,当需要root权限的时候进行sudo操作.以下 ...