qwq

由于窝太菜了,实在是不会,所以在题解的帮助下过掉了这道题。

写此博客来整理一下思路

正文

传送

简化一下题意:现在有\(n\)行\(m\)列数,选\(k\)个数的合法方案需满足:

1.一行最多选一个

2.一列最多选\(\lfloor \frac{k}{2} \rfloor\)个数

当然,如果你在某一行里选了0,就相当于没有在这一行里选数

选一次对答案的贡献是你选的所有不为零的数的乘积。对于任意的\(k\),只要有合法方案,就能取。

(希望没有把题目变得更复杂叭)

根据上面的要求,我们发现\(k\)的取值范围是\([1,n]\)。而且根据要求2,如果某个方案在满足1的前提下,是不合法的,那么这个方案里面一定有且仅有1列选了超出\(\lfloor \frac{k}{2} \rfloor\)个数,因为不可能有两列选的数同时超过\(\lfloor \frac{k}{2} \rfloor\)个。我们现在知道了不合法方案的一个特征,那么我们不妨试试总方案数-不合法方案数这个思路。

因为 满足1情况的总方案数-满足1而且不合法的方案数=乱选方案数-不满足1或不满足2的方案数 ,所以我们接下来计算方案数都在满足1的条件下来计算。

计算总方案数:设\(all[i][j]\)表示前\(i\)行,每行至多选一个,一共选了\(j\)个的方案数,那么\(all[i][j]=all[i-1][j]+\sum_{l=1}^m{all[i-1][j-1] \times a[i][l]}\)。用\(sum[i]\)表示第\(i\)行所有数的和,那么\(all[i][j]=all[i-1][j]+all[i-1][j-1] \times sum[i]\)

我们再来看看不合法方案怎么算。上面说到一个不合法方案一定只有1行选的数超过了\(\lfloor \frac{k}{2} \rfloor\)个,所以我们可以枚举每一列。但是我们不知道\(k\)。那么我们可以设\(no[i][j][l]\)表示前\(i\)行,该列选了\(j\)个,其他列选了\(l\)个。\(no[i][j][l]=no[i-1][j][l]+no[i-1][j-1][l]\times a[i][j]+no[i-1][j][l-1] \times(sum[i]-a[i][j])\)这样就可以由\(j,l\)确定唯一的\(k\)。枚举列:\(O(m)\),枚举\(i\):\(O(n)\),因为选数的个数最多是\(n\),所以枚举\(j,l\)都是\(O(n)\),总复杂度\(O(mn^3)\)

显然是不够的,需要优化。发现我们其实并不需要具体的\(k\),只需要知道当前列和其他列选的数的差值即可。为什么呢?不妨设当前列选的数为\(x+j\)个,其他列选的数为\(x\)个,那么一共选的数就是\(2x+j\)个。这里\(x\)取值任意(只要合法就行),所以可以\(2x+j\)覆盖所有的\(k\)。所以设\(no[i][j]\)表示前\(i\)行,当前枚举的列比其他列多选了\(j\)个的方案数。

\(no[i][j]=no[i-1][j]+no[i-1][j-1] \times a[i][j]+no[i-1][j+1] \times (sum[i]-a[i][j])\)

注意这里有个坑:枚举到第\(i\)行的时候,当前列最多会比其他列少\(i\)个数,所以\(j\)应该从\(-i\)开始枚举,而不是0。考虑到不能出现负下标,所以在代码中将每个下标+n。

如果这个方案是不合法方案,那么对应的\(j\)一定大于0。

最终答案就是\(sum_{j=1}^n{all[n][j]}-\sum_{j=1}^n{no[n][j]}\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline ll read()
{
char ch=getchar();
ll x=0;bool f=0;
while(ch<'0'||ch>'9')
{
if(ch=='-') f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f?-x:x;
}
const ll mod=998244353;
ll n,m,a[209][2009],sum[109],all[209][2009];
ll no[109][2109];
ll ans;
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=read(),sum[i]=(sum[i]+a[i][j])%mod;//保险起见随时随地模一下
all[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=n;j++)
all[i][j]=(all[i-1][j]+all[i-1][j-1]*sum[i]%mod+mod)%mod;
for(int j=1;j<=n;j++)
ans=(ans+all[n][j])%mod;
for(int lie=1;lie<=m;lie++)
{
memset(no,0,sizeof(no));
no[0][n]=1;
for(int i=1;i<=n;i++)
{
for(int j=n-i;j<=n+i;j++)
{
no[i][j]=(no[i-1][j]+no[i-1][j-1]*a[i][lie]%mod+no[i-1][j+1]*(sum[i]-a[i][lie])%mod+mod)%mod;
}
}
for(int j=n+1;j<=2*n;j++)
ans=(ans-no[n][j]+mod)%mod;
}
cout<<ans;
return 0;
}

csp2019 Emiya家今天的饭题解的更多相关文章

  1. CSP2019 Emiya 家今天的饭 题解

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

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

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

  3. CSP2019 Emiya 家今天的饭

    Description: 有 \(n\) 中烹饪方法和 \(m\) 种食材,要求: 至少做一种菜 所有菜的烹饪方法各不相同 同种食材的菜的数量不能超过总菜数的一半 求做菜的方案数. Solution1 ...

  4. [CSP-S2019]Emiya 家今天的饭 题解

    CSP-S2 2019 D2T1 很不错的一题DP,通过这道题学到了很多. 身为一个对DP一窍不通的蒟蒻,在考场上还挣扎了1h来推式子,居然还有几次几乎推出正解,然而最后还是只能打个32分的暴搜滚粗 ...

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

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

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

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

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

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

  8. P5664 Emiya 家今天的饭

    题面 link 前言 去年把我做自闭的一道题,看了一眼题面,发现只有 t1 有点思路,结果写到一半发现自己读错题了,又只能花时间来重构,结果后面的暴力一点都没写(主要是自己当时不会) 然后,这道题还因 ...

  9. 洛谷 P5664 [CSP-S2019] Emiya 家今天的饭

    链接: P5664 题意: 给出一个 \(n*m\) 的矩阵 \(a\),选 \(k\) 个格子(\(1\leq k\leq n\)),每行最多选一个,每列最多选\(⌊\dfrac k2⌋\) 个,同 ...

随机推荐

  1. Java读取利用java.util类Properties读取resource下的properties属性文件

    说明:upload.properties属性文件在resources下 import java.io.IOException;import java.io.InputStream;import jav ...

  2. Navicat连接Mysql11.1.13出现1251错误

    打开Navicat软件,单击左上角[连接]按钮,选择mysql,弹出新建连接,输入相关信息,单击[连接测试],报1251的错误,如下图所示: 根因分析: mysql8 之前的版本中加密规则是mysql ...

  3. oracle 使用问题

    12541: 典型的listener.ora 文件内容: SID_LIST_LISTENER =   (注册到监听器的service name所在区域)  (SID_LIST =    (SID_DE ...

  4. xml_dom解析

    DOM解析(一) 采用dom解析,会将xml文档全部载入到内存当中,然后将xml文档中的所有内容转换为tree上的节点(对象). 优点: 可以随机解析 可以修改文件 可以创建xml文件 缺点: 适合解 ...

  5. CPP标准模板库 随笔

    对于无序容器而言,其次序是不固定的,就算添加一个元素,也可能全盘改变次序.唯一可以保证的是,内容相同的元素会相邻.(eg: 23,1,1,25,3,27,5,7,11,11) 迭代器的种类: 1,前向 ...

  6. java 数组转list asList用法

    https://www.cnblogs.com/zheyangsan/p/6910476.html java中数组转list使用Arrays.asList(T... a)方法. 示例: public ...

  7. 利用java8新特性,用简洁高效的代码来实现一些数据处理

    定义1个Apple对象: public class Apple {    private Integer id;    private String name;    private BigDecim ...

  8. 什么是JavaScript循环结构?

    ㈠什么是循环结构 ⑴什么是循环? 反复一遍又一遍的做着相同(相似)的事情 ⑵循环中的两大要素 ①循环条件:什么时候开始,什么时候结束 ②循环操作:循环体,循环过程中,干了什么 ㈡循环结构—while循 ...

  9. head first 设计模式笔记3-装饰者模式:星巴兹饮料

    开放原则:类应该对扩展开放,对修改关闭. - 上篇博客中的观察者模式中,通过加入新的观察者,我们可以在任何时候扩展主题(Subject),而且不需向主题中添加代码. - 装饰者模式也完全遵循开放原则. ...

  10. HDU 6191 Query on A Tree ( 2017广西邀请赛 && 可持久化Trie )

    题目链接 题意 : 给你一棵树.树上的每个点都有点权.之后有若干次问询.每次问询给出一个节点编号以及一个整数 X .问你以给出节点为根的子树中哪个节点和 X 异或最大.输出这个值 分析 : 看到这种树 ...