Description:

有 \(n\) 中烹饪方法和 \(m\) 种食材,要求:

  • 至少做一种菜
  • 所有菜的烹饪方法各不相同
  • 同种食材的菜的数量不能超过总菜数的一半

求做菜的方案数。

Solution1:考虑 DP

先容斥一下,答案为忽略第三个条件所得的方案数减去每一种食材超过一半的方案数之和。

忽略掉第三个条件之后答案显然是

\[\prod_{i=1}^n(1+\sum_{j=1}^m a_{i,j})-1
\]

减去 1 是去掉一道菜都不做的方案。

枚举每一列超过一半的情况,显然,除这一列外,其他 \(n-1\) 列是一样的。那么对于第 \(col\) 列,设 \(f_{i,j,k}\) 表示前 \(i\) 行,第 \(col\) 列选 \(j\) 个且其他列选 \(k\) 个的方案数。则:

\[f_{i,j,k} = f_{i-1,j,k}\text{(不选)}+a_{i,col}*f_{i-1,j-1,k}+(s_i-a_{i,col})*f_{i,-1,j,k-1}
\]

此时的复杂度是 ,\(O(m)\) 的枚举 \(col\) * \(O(n^3)\) 的 \(DP\), = \(O(mn^3)\) ,可以得到 84pts 的好成绩了

Code:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll; const int N = 101;
const int M = 2001;
const int mod = 998244353;
ll n,m;
ll s[N],a[N][M],f[N][N][N];
ll ans=1; void init()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
scanf("%lld",&a[i][j]);
s[i]=(s[i]+a[i][j])%mod;
}
ans=(ans*(s[i]+1))%mod;
}
ans=(mod-1+ans)%mod;
} int main()
{
init();
for(int col=1;col<=m;++col)
{
memset(f,0,sizeof(f));
f[0][0][0]=1;
for(int i=1;i<=n;++i)
{
for(int j=0;j<=i;++j)
{
for(int k=0;k<=i-j;++k)
{
f[i][j][k]=f[i-1][j][k]+f[i-1][j-1][k]*a[i][col]+f[i-1][j][k-1]*(s[i]-a[i][col]);
f[i][j][k]=(f[i][j][k]%mod+mod)%mod;
}
}
} for(int j=1;j<=n;++j)
{
for(int k=0;k<=n-j;++k)
{
if(k<j) ans=((ans-f[n][j][k])%mod+mod)%mod;
}
}
}
printf("%lld\n",ans);
return 0;
}

Solution2:考虑优化

然后我们发现我们并不关心j和k的具体值。我们只关心他们的差。所以我们可以把后两维压缩成一维。

设 \(f_{i,j}\) 表示前 \(i\) 行,第 \(col\) 列比其他列多选 \(j\) 个的方案数。则:

\[f_{i,j} = f_{i-1,j}\text{(不选)}+a_{i,col}*f_{i-1,j-1}+(s_i-a_{i,col})*f_{i,-1,j+1}
\]

此时的复杂度是 ,\(O(m)\) 的枚举 \(col\) * \(O(n^2)\) 的 \(DP\), = \(O(mn^2)\) ,可以得到 100pts 的好成绩了

这里有一个小技巧就是把每个j都加上n,避免数组负下标的出现。

Code:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll; const int N = 101;
const int M = 2001;
const int mod = 998244353;
ll n,m;
ll s[N],a[N][M],f[N][N*2];
ll ans=1; void init()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
scanf("%lld",&a[i][j]);
s[i]=(s[i]+a[i][j])%mod;
}
ans=(ans*(s[i]+1))%mod;
}
ans=(mod-1+ans)%mod;
} int main()
{
init();
for(int col=1;col<=m;++col)
{
memset(f,0,sizeof(f));
f[0][n]=1;
for(int i=1;i<=n;++i)
{
for(int j=n-i;j<=n+i;++j)//注意dp的范围!
{
f[i][j]=f[i-1][j]+f[i-1][j-1]*a[i][col]+f[i-1][j+1]*(s[i]-a[i][col]);
f[i][j]=(f[i][j]%mod+mod)%mod;
}
} for(int j=1;j<=n;++j)
{
ans=((ans-f[n][n+j])%mod+mod)%mod;
}
}
printf("%lld\n",ans);
return 0;
}

Question:

DP的取值范围问题还是不清楚。

CSP2019 Emiya 家今天的饭的更多相关文章

  1. CSP2019 Emiya 家今天的饭 题解

    这题在考场上只会O(n^3 m),拿了84分.. 先讲84分,考虑容斥,用总方案减去不合法方案,也就是枚举每一种食材,求用它做超过\(\lfloor \frac{k}{2} \rfloor\) 道菜的 ...

  2. csp2019 Emiya家今天的饭题解

    qwq 由于窝太菜了,实在是不会,所以在题解的帮助下过掉了这道题. 写此博客来整理一下思路 正文 传送 简化一下题意:现在有\(n\)行\(m\)列数,选\(k\)个数的合法方案需满足: 1.一行最多 ...

  3. 洛谷P5664 Emiya 家今天的饭 问题分析

    首先来看一道我编的题: 安娜写宋词 题目背景 洛谷P5664 Emiya 家今天的饭[民间数据] 的简化版本. 题目描述 安娜准备去参加宋词大赛,她一共掌握 \(n\) 个 词牌名 ,并且她的宋词总共 ...

  4. 洛谷P5664 Emiya 家今天的饭 题解 动态规划

    首先来看一道题题: 安娜写宋词 题目背景 洛谷P5664 Emiya 家今天的饭[民间数据] 的简化版本. 题目描述 安娜准备去参加宋词大赛,她一共掌握 \(n\) 个 词牌名 ,并且她的宋词总共有 ...

  5. 【NOIP/CSP2019】D2T1 Emiya 家今天的饭

    这个D2T1有点难度啊 原题: 花了我一下午的时间,作为D2T1的确反常 条件很奇怪,感觉不太直观,于是看数据范围先写了个暴力 写暴力的时候我就注意到了之前没有仔细想过的点,烹饪方式必须不同 虽然a很 ...

  6. 【CSP-S 2019】【洛谷P5664】Emiya 家今天的饭【dp】

    题目 题目链接:https://www.luogu.org/problem/P5664 Emiya 是个擅长做菜的高中生,他共掌握 \(n\) 种烹饪方法,且会使用 \(m\) 种主要食材做菜.为了方 ...

  7. 【CSP-S 2019】D2T1 Emiya 家今天的饭

    Description 传送门 Solution 算法1 32pts 爆搜,复杂度\(O((m+1)^n)\) 算法2 84pts 裸的dp,复杂度\(O(n^3m)\) 首先有一个显然的性质要知道: ...

  8. Emiya 家今天的饭

    \(dp_{i,j,k}\)表示前\(i\)种烹饪方法,假设最多的是食材\(j\),食材\(j\)比其他食材多\(k\)次出现 其中\(i \in [1,n],j \in [1,m],k \in [- ...

  9. 【JZOJ6433】【luoguP5664】【CSP-S2019】Emiya 家今天的饭

    description analysis 首先可以知道不符合要求的食材仅有一个,于是可以容斥拿总方案数减去选不合法食材的不合法方案数 枚举选取哪一个不合法食材,设\(f[i][j]\)表示到第\(i\ ...

随机推荐

  1. java知识树

    https://blog.csdn.net/aitaozi11/article/details/79652943 (学习Java的9张思维导图) 文章目录 针对技术栈学习 1. java基础 1.1 ...

  2. 使用node查询数据库(mysql)时,日期格式不对的问题。

    https://blog.csdn.net/chanlingmai5374/article/details/93190983 1.问题场景 数据库里存了 datetime.但 Node 查询出来是这样 ...

  3. Scrapy 命令

    Scrapy提供了两种类型的命令.一种必须在Scrapy项目中运行(针对项目(Project-specific)的命令),另外一种则不需要(全局命令).全局命令在项目中运行时的表现可能会与在非项目中运 ...

  4. MySQLroot密码的恢复方法

    MySQLroot密码的恢复方法 有可能你的系统没有 safe_MySQLd 程序(比如我现在用的 ubuntu操作系统, apt-get安装的MySQL) , 下面方法可以恢复 1.停止MySQLd ...

  5. C语言:判断t所指字符串中的字母是否由连续递增字母组成。-判断一个输入的任何整数n,是否等于某个连续正整数序列之和。-将一副扑克牌编号为1到54,以某种方式洗牌,这种方式是将这副牌分成两半,然后将他们交叉,并始终保持编号1的牌在最上方。

    //判断t所指字符串中的字母是否由连续递增字母组成. #include <stdio.h> #include <string.h> void NONO(); int fun( ...

  6. 解决linux 运行自动化脚本浏览器无法启动问题

    1.前提你的驱动和版本对应无问题时,依旧报未知错误无法启动chrome 解决方法加上两行: options.addArguments("no-sandbox");options.a ...

  7. 刷题64. Minimum Path Sum

    一.题目说明 题目64. Minimum Path Sum,给一个m*n矩阵,每个元素的值非负,计算从左上角到右下角的最小路径和.难度是Medium! 二.我的解答 乍一看,这个是计算最短路径的,迪杰 ...

  8. 「JSOI2013」贪心的导游

    「JSOI2013」贪心的导游 传送门 多次询问区间内%一个数的最大值 我们不妨设这个数为M_sea 值域比较小所以考虑分块维护. 我们观察到对于给定的一个 \(p\) ,函数 \(y = x \% ...

  9. acm数论之旅(转载) -- 快速幂

    0和1都不是素数,也不是合数. a的b次方怎么求 pow(a, b)是数学头文件math.h里面有的函数 可是它返回值是double类型,数据有精度误差 那就自己写for循环咯 LL pow(LL a ...

  10. 2016-2017学年第三次测试赛 习题H MCC的考验

    问题 H: MCC的考验 时间限制: 1 Sec  内存限制: 128 MB 题目描述 MCC男神听说新一期的选拔赛要开始了,给各位小伙伴们带来了一道送分题,如果你做不出来,MCC会很伤心的. 给定一 ...