题目:
题意:
有一个
n
∗
m
n*m
n∗m的矩阵,
(
x
,
y
)
(x,y)
(x,y)表示由
x
x
x方法和
y
y
y食材做出的菜有多少种
我们可以选择一些菜来制作,但需要满足一些要求:
1.
1.
1.至少做一道菜
2.
2.
2.任意两种菜的方法不能相同
3.
3.
3.用同一食材做出的菜不能超过
n
/
2
n/2
n/2道
问还有多少种方案
分析:
前两个要求都不是事,但问题在第三个要求,我们貌似直接求的话不好设置状态
正所谓正难则反,我们通过计算不合法的方案和总方案,就可以得到我们希望的答案了
不合法:
因为无论如何,都只会是一种食材不合法,不可能存在两种食材都做出了超过
n
/
2
n/2
n/2的菜
所以我们可以把这一个食材拎出来,其他的食材无论如何分配都不会影响到合法性
设
f
i
,
j
f_{i,j}
fi,j表示已经考虑到第
i
i
i种方法,而我们假定不合法这一食材与其他食材做出的菜的差为
j
j
j,因为每种方法都至多有一道菜,所以转移挺好写的
最后不合法的方案我们就去
j
>
n
/
2
j>n/2
j>n/2里面找
全部:
这多简单呐,莫得任何限制,设
g
i
,
j
g_{i,j}
gi,j表示考虑到第
i
i
i种方法,选择了
j
j
j种方法的方案数
对每种方案的菜品的数目求个和,都可以愉快计算
最后的答案是
∑
i
=
1
n
g
n
,
i
−
∑
i
=
n
/
2
+
1
n
f
n
,
i
\sum_{i=1}^ng_{n,i}-\sum_{i=n/2+1}^nf_{n,i}
∑i=1ngn,i−∑i=n/2+1nfn,i
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define mo 998244353
using namespace std;
inline LL read()
{
LL s=0,f=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {s=s*10+c-'0';c=getchar();}
return s*f;
}
LL a[105][2005],s[105][2005];
LL f[105][205],g[105][105];
int main()
{
LL n=read(),m=read();
for(LL i=1;i<=n;i++)
{
LL sum=0;
for(LL j=1;j<=m;j++) (sum+=a[i][j]=read())%=mo;
for(LL j=1;j<=m;j++) s[i][j]=(sum-a[i][j]+mo)%mo;
s[i][0]=sum; g[i][0]=1;
}
LL r=0;
for(LL e=1;e<=m;e++)
{
memset(f,0,sizeof(f));
f[0][n]=1;
for(LL i=1;i<=n;i++)
for(LL j=n-i;j<=n+i;j++)
f[i][j]=(f[i-1][j]+a[i][e]*f[i-1][j-1]%mo+s[i][e]*f[i-1][j+1]%mo)%mo;
for(LL i=n+1;i<=2*n;i++) (r+=f[n][i])%=mo;
}
g[0][0]=1;
for(LL i=1;i<=n;i++)
for(LL j=1;j<=i;j++)
g[i][j]=(g[i-1][j]+g[i-1][j-1]*s[i][0]%mo)%mo;
LL sum=0;
for(LL i=1;i<=n;i++) (sum+=g[n][i])%=mo;
cout<<(sum-r+mo)%mo;
return 0;
}