CSP2019 Emiya 家今天的饭

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的取值范围问题还是不清楚。

原文地址:https://www.cnblogs.com/oierwyh/p/12267569.html

时间: 2024-10-08 16:40:48

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

csp2019 Emiya家今天的饭题解

qwq 由于窝太菜了,实在是不会,所以在题解的帮助下过掉了这道题. 写此博客来整理一下思路 正文 传送 简化一下题意:现在有\(n\)行\(m\)列数,选\(k\)个数的合法方案需满足: 1.一行最多选一个 2.一列最多选\(\lfloor \frac{k}{2} \rfloor\)个数 当然,如果你在某一行里选了0,就相当于没有在这一行里选数 选一次对答案的贡献是你选的所有不为零的数的乘积.对于任意的\(k\),只要有合法方案,就能取. (希望没有把题目变得更复杂叭) 根据上面的要求,我们发现

CSP-S 2019 Emiya 家今天的饭

\(\text {Emiya 家今天的饭}\) Emiya 不会让大家饿肚子,所以将做至少一道菜,即 \(k\geq1\) Rin 希望品尝不同烹饪方法做出的菜,因此她要求每道菜的烹饪方法互不相同 Yazid 不希望品尝太多同一食材做出的菜,因此他要求每种主要食材至多在一半的菜(即\(?k /2?\)道菜)中被使用 subtask1 \(\text {期望得分 64pts}\) \(f[i][j][k][l]\)\(j, k, l\)分别表示每一列选多少数就行了 subtask2 \(\text

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

description analysis 首先可以知道不符合要求的食材仅有一个,于是可以容斥拿总方案数减去选不合法食材的不合法方案数 枚举选取哪一个不合法食材,设\(f[i][j]\)表示到第\(i\)种烹饪方法.操作权值为\(j\)的方案数 给每一个操作赋权值,选当前行合法食材列为\(0\),不选当前行为\(1\),选当前行不合法食材列为\(2\) 转移是比较容易的,可知选当前列为不合法食材的方案数就是\(\sum_{i=n+1}^{2n}f[n][i]\) code #include<std

CSP2019 题解

CSP2019 题解 D1T1 格雷码(code) 题目传送门 https://loj.ac/problem/3208 题解 按照题意模拟就可以了. 对于第 \(i\) 位,如果 \(k \geq 2^i\) 那么这一位就是 \(1\),然后把 \(k\) 变成 \(2^{i + 1} - k - 1\).否则这一位为 \(0\),\(k\) 不变. 代码 https://loj.ac/submission/687508 D1T2 括号树(brackets) 题目传送门 https://loj.

CSP2019题解

按照CSP题目顺序来写 格雷码 不难发现答案可以递归找到,然后每一次做即可. 代码 括号树 简单题,直接在树上搞一个栈然后回溯即可. 括号树nmsl 代码 树上的数 咕咕咕 Emiya 家今天的饭 很显然可以看出这题可以容斥,然后就可以写一个\(O(mn^3)\)的\(dp\). 然后考虑后面那两维状态可以做一个差,这样子就优化成了\(O(2mn^2)\),然后就可以过了. 代码 划分 \(\texttt{__int128}\)不香吗 代码 树的重心 代码 原文地址:https://www.cn

CSP2019复赛游记

目录 Day -1 Day 0 Day 1 考前 考中 考后 做法&程序(考场做法,不能AC) 格雷码 括号树 树上的数 Day 2 考前 考中 考后 做法&程序(考场做法,不能AC) Emiya 家今天的饭 划分 树的重心 Day 3+(后记&提醒) date: 2019-11-15 前言:想直接进入正题请跳转到Day 1. Day -1 明天我就去南京了,感到非常的紧张,毕竟CSP和NOIP没有关系,没有真题可以刷(逃. 注:对于内心中难度的比较,是对于NOIP提高组的题目比较

CSP-J/S2019试题选做

小蒟蒻duyi CSP爆炸之后一直在颓,所以直到最近才陆续订正完CSP的题/kel 以后不能这么颓了(flag) S D1T2 括号树 设\(f[u]\)表示根到\(u\)的路径上有多少子串是合法括号串.(即题目里的\(k_u\),此变量名缺乏个性,故换之) 从根向每个节点dfs,容易求出\(c[u]\):表示从根到\(u\)的路径上,我们能匹配则匹配,最后剩下多少个待匹配的左括号. 例如如下\(s_u\)对应的\(c[u]\): ((() \(c[u]=2\). (())( \(c[u]=1\

张书乐:想在中国开149家店,宜家的肉丸生意快做不下去了

在后流量时代,宜家对更多场景的渴望,已经不是一个店面能包容的了.它不得不依托巨大的线下人流,寻找更多市场红利. 文/张书乐 刊载于<商界评论>2017年2月刊 据宜家家居公布的2016财年报告显示,宜家集团总收入达351亿欧元,比上年增长了7.4%:宜家中国销售额超过125亿人民币,比于去年增长了18.9%.宜家集团今年全球商场共计迎来7.83亿人次的到访,而宜家中国每分钟就会迎来339位顾客,比去年增加20%.在该公司全球28个市场中,中国增长最快. 一连串利好的上扬数据,尤其是在中国市场的

周爱民:真正的架构师是没有title的(图灵访谈)

周爱民,现任豌豆荚架构师,国内软件开发界资深软件工程师.从1996年起开始涉足商业软件开发,历任部门经理.区域总经理.高级软件工程师.平台架构师等职,有18年的软件开发与架构.项目管理及团队建设经验,曾任盛大网络平台架构师.支付宝业务架构师,是 Borland Delphi 产品技术专家,也是 Qomo 开源项目(JavaScript)的发起者.2003年5月被美国 Borland 公司授予「Borland Delphi 产品专家」称号,并授予「论坛特别贡献奖」.著有<大道至简——软件工程实践者