题解:Luogu P5664 [CSP-S2019] Emiya 家今天的饭
一些闲话
原题链接
挺好的一道 DP 题,容斥+压缩维度优化 DP。
题意简述
Emiya 是个擅长做菜的高中生,他共掌握
Emiya 今天要准备一桌饭招待 Yazid 和 Rin 这对好朋友,然而三个人对菜的搭配有不同的要求。对于一种包含
- Emiya 将做至少一道菜,即
; - Rin 希望品尝不同烹饪方法做出的菜,因此她要求每道菜的烹饪方法互不相同;
- Yazid 不希望品尝太多同一食材做出的菜,因此他要求每种主要食材至多在一半的菜(即
道菜)中被使用。
Emiya 想知道共有多少种不同的符合要求的搭配方案。两种方案不同,当且仅当存在至少一道菜在一种方案中出现,而不在另一种方案中出现。
请你帮他计算符合所有要求的搭配方案数对质数
对于所有测试点,
题解
题目差不多就是在网格里选位置的方案数。
这题主要的难度在于第三个要求是难以刻画的。所以正难则反,我们考虑容斥,用总方案数减去不合法的方案数。不合法的方案意味着有且仅有一列被选择的数量超过了总数量的一半。
令
容易想到 DP 求不合法的方案数。我们枚举被选择的数量超过了总数量一半的那一列
转移时注意
时间复杂度为
考虑进一步优化。这一步是比较巧妙的:我们注意到我们只关心
所以我们无需枚举
其中
时间复杂度少了一个
代码
#include <iostream>
#include <cstring>
using namespace std;
#define add_mod(x, v) (x) = ((ll)(x) + (v)) % MOD
#define sub_mod(x, v) (x) = (((ll)(x) - (v)) % MOD + MOD) % MOD
#define mul_mod(x, v) (x) = (1ll * (x) * (v)) % MOD
typedef long long ll;
typedef pair<int, int> pii;
const int MOD = 998244353;
const int MAX_N = 105, MAX_M = 2e3 + 5;
int n, m, a[MAX_N][MAX_M];
ll s[MAX_N], tot = 1, f[MAX_N][MAX_N << 1];
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> a[i][j];
add_mod(s[i], a[i][j]);
}
mul_mod(tot, s[i] + 1);
}
sub_mod(tot, 1);
for (int c = 1; c <= m; ++c) {
memset(f, 0, sizeof(f));
f[0][n] = 1;
for (int i = 1; i <= n; ++i)
for (int d = -i; d <= i; ++d) {
add_mod(f[i][d + n], f[i - 1][d + n]);
add_mod(f[i][d + n], f[i - 1][d - 1 + n] * a[i][c] % MOD);
add_mod(f[i][d + n], f[i - 1][d + 1 + n] * (s[i] - a[i][c]) % MOD);
}
for (int d = 1; d <= n; ++d) sub_mod(tot, f[n][d + n]);
}
cout << tot << '\n';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下