链接:

P5664


题意:

给出一个 \(n*m\) 的矩阵 \(a\),选 \(k\) 个格子(\(1\leq k\leq n\)),每行最多选一个,每列最多选\(⌊\dfrac k2⌋\) 个,同时每个格子有 \(a_{i,j}\) 种不同选法,问共有多少种不同的选法,对 \(998244353\) 取模。给出 \(n,m\) 和 矩阵 \(a\)。


分析:

尝试直接 dp 失败后看了题解。这是道 dp 和容斥的好题。

考虑列的限制,每列最多选\(⌊\dfrac k2⌋\) 个,意味着最多只有一列不合法,所以我们可以枚举不合法的列,似乎能更容易地算出不合法的方案数。

所以考虑算出不计列限制的方案数,减去列限制不合法的方案数来容斥得到答案。


算法:

对于列限制不合法的方案数。

首先枚举不合法的列数 \(c\)。

dp 的设计很巧妙,由于我们只关心第 \(c\) 列,其他列之间的区别不重要,所以将其他列融合成一个状态。设 \(f[i,j,k]\) 为前 \(i\) 行,第 \(c\) 列选了 \(j\) 个,其他列选了 \(k\) 个的方案数。

分成第 \(i\) 行不选,选第 \(c\) 列,选其他列来转移。

选第 \(c\) 列的方案有 \(a_{i,c}\) 种,则选其他列的方案有 \(\sum\limits_{j=1}^m a_{i,j}-a_{i,c}\) 种,所以我们需要用一个 \(s\) 数组预处理第 \(i\) 行所有 \(a\) 之和。

\(f[i,j,k]=f[i-1,j,k]+a_{i,c}*f[i-1,j-1,k]+(s_i-a_{i,c})*f[i-1,j,k-1]\)

最后的答案要满足第 \(c\) 列不合法且至少选一个,所以是 \(\sum\limits_{j=1}^n\sum\limits_{k=0}^{\min(j-1,n-j)}f[n,j,k]\)。(\(j-1\) 保证 \(j>k\) ,\(n-j\) 保证 \(j+k\leq n\))

初值是 \(f[0,0,0]=1\),状态数是 \(n^3\) 枚举了 \(m\) ,所以时间复杂度是 \(O(n^3m)\)。

对于不计列限制的方案数。

只限制每行最多选一个,我们可以运用组合数学,每行的方案数是 \(s_i+1\) (选或不选),所以只用把所有 \(s_i+1\) 乘起来,又因为 \(k\geq1\) ,所以最后减掉一个完全没有选的方案即可。即 \(\prod\limits_{i=1}^n(s_i+1)-1\)。

时间复杂度 \(O(n)\)。


优化:

上面的算法时间复杂度 \(O(n^3m)\),可以获得84分的好成绩。

于是对于列限制不合法的 dp 又有了一个更神仙的优化:我们发现要判断不合法方案不需要 \(j\) 和 \(k\) 的具体数值而只需要他们的相对大小,所以将“第 \(c\) 列选了 \(j\) 个,其他列选了 \(k\) 个”的状态再融合成"第 \(c\) 列比其他列多选 \(j\) 个",那么状态转移方程变为\(f[i,j]=f[i-1,j]+a_{i,c}*f[i-1,j-1]+(s_i-a_{i,c})*f[i-1,j+1]\)。

由于 \(j\) 会有负数的情况,所以将 dp 第二维参与操作时全部加上 \(n\)。

初值是 \(f[0,0+n]=1\),时间复杂度降为 \(O(n^2m)\),可以通过此题。


代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define in read()
inline int read(){
int p=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){p=p*10+c-'0';c=getchar();}
return p*f;
}
const int mod=998244353;
int n,m;
int a[105][2005];
int s[105];
int f[105][205];
int ans1,ans2;
inline int add(int a,int b){return (a+b)%mod;}
inline int mul(int a,int b){return a*b%mod;}
signed main(){
n=in,m=in;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
a[i][j]=in;
s[i]=add(s[i],a[i][j]);
}
for(int c=1;c<=m;c++){
memset(f,0,sizeof(f));
f[0][n+0]=1;
for(int i=1;i<=n;i++)
for(int j=-i;j<=i;j++){
f[i][n+j]=f[i-1][n+j];
if(j>-i)f[i][n+j]=add(f[i][n+j],mul(f[i-1][n+j-1],a[i][c]));
if(j<i)f[i][n+j]=add(f[i][n+j],mul(f[i-1][n+j+1],((s[i]-a[i][c])%mod+mod)%mod));
}
for(int j=1;j<=n;j++)
ans1=add(ans1,f[n][n+j]);
}
ans2=1;
for(int i=1;i<=n;i++)
ans2=mul(ans2,s[i]+1);
ans2-=1;
cout<<((ans2-ans1)%mod+mod)%mod;
return 0;
}
题外话:

这个 dp 加容斥好难啊我趣

洛谷 P5664 [CSP-S2019] Emiya 家今天的饭的更多相关文章

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

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

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

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

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

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

  4. 洛谷 P5664 Emiya 家今天的饭(84分)

    题目传送门 解题思路: 对于每一个列c,f[i][j][k]表示到第i行,第c列选了j个,其它列一共选了k个,然后我们读题意发现只要j>k,那就一定是不合法的,然后统计所有方案,减去所有不合法方 ...

  5. 洛谷 P3258 松鼠的新家 题解

    题面 貌似这道题暴力加玄学优化就可以AC? 下面是正解: 1.树链剖分: 我们在u到v之间都放一个糖果,可以将松鼠它家u到v的糖果数都加1.每一次将a[i]到a[i+1] (a数组是访问顺序)的节点加 ...

  6. 洛谷 [P3258] 松鼠的新家

    树上差分 对于一条路径 \(u->v\) 来说,设 \(t=LCA(u,v)\) ,d[]为差分数组 ,则有 d[u]++;d[v]++;d[t]--;d[fa[t]]--; 注意:题目中所给的 ...

  7. 洛谷P3258 松鼠的新家

    树上差分 这应该是一道很简单的树上差分了..就是问每个点被覆盖了多少次. 要注意我们最后dfs后,要把除第一个节点以外的所有点的-1,因为有些点作为起点和终点覆盖了两次,按照题目意思是不用覆盖两次的. ...

  8. 洛谷P3258松鼠的新家

    题目传送门 恩,很明显的一个树剖题,配合树上差分其实也并不难,不过无奈蒟蒻树剖还没那么熟练,而且树上差分也做的少,所以这题愣是做了一中午......唉,果然我还是太菜了.恩,具体做法在代码中解释吧: ...

  9. Emiya家今天的饭 NOIP2019 (CSP?) 类DP好题 luoguP5664

    luogu题目传送门! 首先,硬求可行方案数并不现实,因为不好求(去年考场就这么挂的,虽然那时候比现在更蒟). 在硬搞可行方案数不行之后,对题目要求的目标进行转换: 可行方案数 = 总方案数 - 不合 ...

随机推荐

  1. HDU 6170 Two strings( DP+字符串匹配)

    http://acm.hdu.edu.cn/showproblem.php?pid=6170 题目大意: 给出两个字符串s1和s2(长度小于等于2500). s1是一个正常的包含大小写字母的字符串,s ...

  2. .NET 中的HTTP 3支持

    dotnet团队官方博客发布了一篇HTTP3的文章:HTTP/3 support in .NET 6.文章介绍了.NET 6 将预览支持HTTP3,.NET 7正式支持HTTP3,原因主要是HTTP/ ...

  3. Linux系列(24) - chmod

    前言 在Unix和Linux的中,每个文件(文件夹也被看作是文件)都有三种权限:读.写.运行. 被授予权限的用户身份有三种:当前文件的拥有者,与拥有者属于同组者(同一个group),其他人 hello ...

  4. openldap集成ssh 登录

    一 安装nslcd服务 yum install nss-pam-ldapd 二.修改vi /etc/nslcd.conf这个配置文件 修改uri 和base的值 改为你的ldap的地址和用户名 三.  ...

  5. 🧚‍♂️全套Java教程_Java基础入门教程,零基础小白自学Java必备教程👨‍💻004 # 第四单元 流程控制语句上 #

    一.本单元知识点概述 二.本单元目标 (Ⅰ)重点知识目标 1.if语句的格式及执行流程2.switch语句的格式及执行流程 (Ⅱ)能力目标 1.掌握if语句的格式及执行流程2.掌握switch语句的格 ...

  6. Winform 实现图片轮播(解决Image.FromFile内存不足)

    前言 最近项目中需要在winform中做一个类似于网页那种轮播的效果,这里做下记录. 实现 整体的实现思路如下: 读取图片文件夹. 建立一个集合存储Image对象. 定时器定时更换PictrueBox ...

  7. HCNP Routing&Switching之路由引入

    前文我们了解了路由控制技术策略路由相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/15333139.html:今天我们来聊一聊路由引入技术相关话题: 路 ...

  8. Java面向对象编程(二)

    关键字 -- this 一.this关键字的使用: 1.this可以用来修饰.调用:属性.方法.构造器. 2.this修饰属性和方法: this理解为:当前对象 或 当前正在创建的对象. 2.1 在类 ...

  9. Mysql 5.7版本,所有的坑,这里都有

    MYSQL5.7版本流程的坑,我这里都有 必须按照如下操作.不按照下面操作,出错误不要怪我哦_ 我们首先在官网下载mysql5.7版本 解压之后,在bin相同目录下创建一个my.ini配置文件里面内容 ...

  10. 10.6 Nginx 高并发连接

    Nginx 高并发连接 什么是IO,输入输出      Web服务器IO的整个详细过程             (1)客户发起请求到服务器网卡:         (2)服务器网卡接受到请求后转交给内核 ...