题目
思路
分类讨论,哪一种主要食材使用了超过 ⌊ k 2 ⌋ \lfloor\frac{k}{2}\rfloor ⌊2k⌋。明显,这样的食材只有一种,所以无需容斥。
然后没了。状态定义见代码。
# pragma GCC optimize("Ofast")
// 不加竟然会T.可能是常数不太优秀
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
inline int readint(){
int a = 0, f = 1; char c = getchar();
for(; c<'0' or c>'9'; c=getchar())
if(c == '-') f = -1;
for(; '0'<=c and c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
void writeint(int x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) writeint(x/10);
putchar(x%10+'0');
}
const int Mod = 998244353;
int a[101][2001], n, m;
void input(){
n = readint(), m = readint();
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j)
a[i][j] = readint();
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j)
a[i][0] = (a[i][0]+a[i][j])%Mod;
}
namespace WORK1{
int f[41][41]; // 一号暴力:
void Main(){ // 针对m = 2
f[0][0] = 1; // 做了i道菜,1号食材用了j次
for(int i=1; i<=n; ++i)
for(int j=i; ~j; --j)
for(int k=j; ~k; --k){
if(j and k){
f[j][k] += 1ll*f[j-1][k-1]*a[i][1]%Mod;
f[j][k] %= Mod;
}
if(j){
f[j][k] += 1ll*f[j-1][k]*a[i][2]%Mod;
f[j][k] %= Mod;
}
}
int ans = 0;
for(int i=2; i<=n; i+=2)
ans = (ans+f[i][i/2])%Mod;
writeint(ans); putchar('\n');
}
}
namespace WORK2{
int f[41][41][41]; // 二号暴力:
void Main(){ // 针对m = 3
f[0][0][0] = 1; // 做了i道菜,j个1号食材,k个2号食材
for(int i=1; i<=n; ++i)
for(int j=i; ~j; --j)
for(int k=j; ~k; --k)
for(int l=j-k; ~l; --l){
if(j and k){
f[j][k][l] += 1ll*f[j-1][k-1][l]*a[i][1]%Mod;
f[j][k][l] %= Mod;
}
if(j and l){
f[j][k][l] += 1ll*f[j-1][k][l-1]*a[i][2]%Mod;
f[j][k][l] %= Mod;
}
if(j){
f[j][k][l] += 1ll*f[j-1][k][l]*a[i][3]%Mod;
f[j][k][l] %= Mod;
}
}
int ans = 0;
for(int i=1; i<=n; ++i)
for(int j=0; j<=(i/2); ++j)
for(int k=((i+1)/2)-j; j+k<=i and k<=(i/2); ++k)
ans = (ans+f[i][j][k])%Mod;
writeint(ans); putchar('\n');
}
}
namespace WORK{
int f[2][2002]; // 滚动
void Main(){
int ans = 1;
for(int i=1; i<=n; ++i)
ans = (1ll*ans*(a[i][0]+1))%Mod;
ans = (Mod+ans-1)%Mod; // 乱选的情况
// -1是去掉一道菜都不做
for(int j=1; j<=m; ++j){
for(int i=0; i<=(n<<1); ++i)
f[0][i] = 0;
f[0][n] = 1;
// f[i]: i = n+菜的数量-(用第j种的*2)
for(int i=1; i<=n; ++i){
for(int k=0; k<=(n<<1); ++k)
f[i&1][k] = 0;
for(int k=0; k<=(n<<1); ++k){
f[i&1][k+1] = (f[i&1][k+1]+1ll*(a[i][0]-a[i][j]+Mod)*f[(i&1)^1][k]%Mod)%Mod;
// 不用j做菜
if(k)
f[i&1][k-1] = (f[i&1][k-1]+1ll*a[i][j]*f[(i&1)^1][k]%Mod)%Mod;
// 用j做菜
f[i&1][k] = (f[i&1][k]+f[(i&1)^1][k])%Mod;
// 不做菜
}
}
// i < n <==> n+菜的数量-(用第j种的*2) < n
// ==> 菜的数量-(用第j种的*2) < 0
// ==> 菜的数量/2 < 用第j种的
for(int i=0; i<n; ++i)
ans = (ans-f[n&1][i]+Mod)%Mod;
}
writeint(ans), putchar('\n');
}
}
void solve(){
if(m == 2){
WORK1::Main();
return ;
}
if(m == 3){
WORK2::Main();
return ;
}
WORK::Main();
}
int main(){
// freopen("meal.in","r",stdin);
// freopen("meal.out","w",stdout);
input();
solve();
return 0;
}