一、题目
二、解法
0x01 暴力
可以考虑搜索,时间复杂度
O
(
m
n
)
O(m^n)
O(mn),可以拿到
32
32
32分的高分。
考虑
m
≤
3
m\leq 3
m≤3的部分分,直接四维
d
p
dp
dp,定义
d
p
[
i
]
[
j
]
[
k
]
[
l
]
dp[i][j][k][l]
dp[i][j][k][l]为前
i
i
i行分别选
j
,
k
,
l
j,k,l
j,k,l个,转移也不难,时间复杂度
O
(
n
4
m
)
O(n^4m)
O(n4m),可以拿到
60
60
60分的高分。
0x02 正解
首先有一个显然的想法,先考虑第一个条件,然后再容斥第二个条件,考虑题目给的
1
/
2
1/2
1/2,也就意味着我们可以
全
部
−
每
种
主
菜
超
过
1
/
2
的
方
案
数
全部-每种主菜超过1/2的方案数
全部−每种主菜超过1/2的方案数,易发现这种计算方法不重不漏。
现在就有两种思路,第一种是排列组合的思想,由于给的
a
a
a太恶心,这种方法应该是难做的。既然排列组合做不出来,考虑计数
d
p
dp
dp,我们先枚举钦定的主菜,定义
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]为在钦定这种主菜的情况下前
i
i
i行中选
j
j
j个,钦定的主菜选了
k
k
k个,转移略。
这样已经能拿到
84
84
84的高分了,如果你有职业精神
,那就继续考虑优化,优化主要是要把
j
j
j那一维给优化掉,考虑改变状态定义
d
p
[
i
]
[
k
]
dp[i][k]
dp[i][k]为前
i
i
i行的操作权值为
k
k
k的总方案,我们给三种操作赋权值,如果不选,权值为
1
1
1;如果选了其他主菜,权值为
0
0
0;如果选了当前主菜,权值为
2
2
2,可以发现我们最后要求的就是
∑
d
p
[
n
]
[
k
]
\sum dp[n][k]
∑dp[n][k],
(
n
+
1
≤
k
≤
2
n
)
(n+1\leq k\leq 2n)
(n+1≤k≤2n),转移如下:(
s
u
m
sum
sum是当前行的
a
a
a求和,我们已枚举了主菜
j
j
j)。
{ d p [ i ] [ k ] + = d p [ i − 1 ] [ k ] × ( s u m [ i ] − a [ i ] [ j ] ) d p [ i ] [ k + 1 ] + = d p [ i − 1 ] [ k ] d p [ i ] [ k + 2 ] + = d p [ i − 1 ] [ k ] × a [ i ] [ j ] \begin{cases} dp[i][k]+=dp[i-1][k]\times (sum[i]-a[i][j]) \\ dp[i][k+1]+=dp[i-1][k] \\ dp[i][k+2]+=dp[i-1][k]\times a[i][j] \end{cases} ⎩⎪⎨⎪⎧dp[i][k]+=dp[i−1][k]×(sum[i]−a[i][j])dp[i][k+1]+=dp[i−1][k]dp[i][k+2]+=dp[i−1][k]×a[i][j]
时间复杂度 O ( m n 2 ) O(mn^2) O(mn2)。
#include <cstdio>
#include <cstring>
#define int long long
const int MAXN = 105;
const int MAXM = 1005;
const int MOD = 998244353;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^'0'),c=getchar();
return x*flag;
}
int n,m,a[MAXN][MAXM*2],sum[MAXN];
int ans=1,dp[MAXN][MAXN*2];
signed 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]+=a[i][j];
sum[i]%=MOD;
}
for(int i=1;i<=n;i++)
ans*=(sum[i]+1),ans%=MOD;
ans--;
for(int j=1;j<=m;j++)
{
for(int i=0;i<=n;i++)
for(int k=0;k<=2*n;k++)
dp[i][k]=0;
dp[0][0]=1;
for(int i=1;i<=n;i++)
for(int k=0;k<=2*n;k++)
{
dp[i][k]+=dp[i-1][k]*(sum[i]-a[i][j]),dp[i][k]%=MOD;
dp[i][k+1]+=dp[i-1][k],dp[i][k+1]%=MOD;
dp[i][k+2]+=dp[i-1][k]*a[i][j],dp[i][k+2]%=MOD;
}
for(int i=n+1;i<=2*n;i++)
ans-=dp[n][i],ans%=MOD;
}
printf("%lld\n",(ans%MOD+MOD)%MOD);
}