太难惹!!!
题目描述
Emiya 是个擅长做菜的高中生,他共掌握 n 种烹饪方法,且会使用 m 种主要食材做菜。为了方便叙述,我们对烹饪方法从 1∼n 编号,对主要食材从 1∼m 编号。
Emiya 做的每道菜都将使用恰好一种烹饪方法与恰好一种主要食材。更具体地,Emiya 会做 道不同的使用烹饪方法 i 和主要食材 j 的菜(1≤i≤n,1≤j≤m),这也意味着 Emiya 总共会做
道不同的菜。
Emiya 今天要准备一桌饭招待 Yazid 和 Rin 这对好朋友,然而三个人对菜的搭配有不同的要求,更具体地,对于一种包含 k 道菜的搭配方案而言:
- Emiya 不会让大家饿肚子,所以将做至少一道菜,即 k≥1
- Rin 希望品尝不同烹饪方法做出的菜,因此她要求每道菜的烹饪方法互不相同
- Yazid 不希望品尝太多同一食材做出的菜,因此他要求每种主要食材至多在一半的菜(即
道菜)中被使用
这里的 为下取整函数,表示不超过 x 的最大整数。
这些要求难不倒 Emiya,但他想知道共有多少种不同的符合要求的搭配方案。两种方案不同,当且仅当存在至少一道菜在一种方案中出现,而不在另一种方案中出现。
Emiya 找到了你,请你帮他计算,你只需要告诉他符合所有要求的搭配方案数对质数 998,244,353 取模的结果。
数据范围:
对于所有测试点,保证
一、分析
1.方法
由题目可知为求解方案数的问题转化为DP/组合数问题。分析题目中的数据 发现给我们提供了大量的状态直接选择DP。通过三个限制条件发现yazid的条件最难完成 所以从yazid 的条件入手,即维护每一列上的菜数不超过总数的一半。但是发现如果进行对于每一列的维护空间时间都不够。由正难则反易得,使用容斥原理,先计算出总体的所有方案数(乘法原理)再计算出每一列不符合的方案数进行差值的计算即可得出答案。
显然,在一个不成立的列方案中,必有一列的节点大于挑选的总结点,因此可以枚举这个超过限制的列,然后进行DP。
2.状态及状态转移方程
f[line][j][k]中 i表示已经在这一列枚举到第i行,j表示本列挑选了多少个菜,k表示其他列挑选了多少个菜,对于每个列复杂度为则总时间复杂度为
显然已经超过了时间复杂度。
满分的话还需要进一步优化,对于j和k而言不需要知道具体的数值,由于条件为并且j+k=sum,则只要
说明
。时间复杂度变为
状态转移方程为f[line][c]=f[line-1][c]+f[line-1][c-1]*a[line][row]+f[line-1][c+1]*(sum[line]-a[line][row])//分别对应1.直接从上一行转移2.从上一行过来在本列选择一个3.从上一行过来在其他列选择。
3.注意事项
每次计算列的时候要memset,取模的时候注意不要出现负数,记得开long long!!!
十年OI一场空,不开longlong见祖宗!
二、代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define N 1000010
#define mod 998244353
using namespace std;
ll n,m,ans=1;
ll h[110][2010],cnt[10010],sum[210],f[210][5010];
int main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;++i)
{
for(ll j=1;j<=m;++j)
{
scanf("%lld",&h[i][j]);
sum[i]=(sum[i]+h[i][j])%mod;//前缀和求出每行的总数
}
ans=(ans*(sum[i]+1))%mod;//计算总方案数
}
for(int row=1;row<=m;++row)
{
memset(f,0,sizeof(0));
f[0][n]=1;//重置 为n为了防止负数下标
for(int line=1;line<=n;++line)
for(int c=n-line;c<=n+line;++c)//j<k转移到j>k 但都是基于f[0][n]开始
{
f[line][c]=((f[line-1][c]+(f[line-1][c-1]*h[line][row])%mod)%mod+f[line-1][c+1]*(sum[line]-h[line][row])%mod)%mod;
}
for(int line=n+1;line<=2*n;++line)
ans=(ans-f[n][line]+mod)%mod;//计算容斥
//!!!!取模
}
printf("%lld\n",ans-1);
return 0;
// 华丽结束
}
总结
本文仅仅简单介绍了DP的判断以及推出状态转移方程,但是其他神犇还有更加玄学的做法,详情请移步洛谷。
这是一篇关于解决组合优化问题的文章,以CSP-S2019题目「Emiya 家今天的饭」为例,讨论如何在满足特定条件(如烹饪方法互不相同,食材不超过一半)下计算不同菜谱方案的数量。文章通过分析题目数据,提出使用动态规划(DP)的方法,并给出了状态转移方程。同时,作者强调了在处理此类问题时要注意的细节,如避免状态空间爆炸和正确处理取模运算。

4万+

被折叠的 条评论
为什么被折叠?



