[CSP2019] Emiya 家今天的饭
题面:Luogu
题解:容斥+DP
首先题意是这样的:
给一个\(n\times m\)的矩阵,每一行只能选一个,每一列选的总数不能超过选的总数的一半。求总方案数
考虑容斥:总方案数减去不合法方案数
总方案数显然:设每一行的总数为\(sum_i\)
\[ans=\prod_{i=1}^{n} (sum[i]+1)-1
\]
减一是因为不能不做一个菜
由于不合法的列必然只有一个,所以我们枚举这一列\(col\)
设\(f_{i,j,k}\)表示前\(i\)行在\(col\)列中选了\(j\)个,其他列中选了\(k\)个
则
\[f_{i,j,k}=f_{i-1,j,k}+a_{i,col}f_{i-1,j-1,k}+(sum[i]-a_{i,col})f_{i-1,j,k-1}
\]
\[ans-=\sum_{j>k}f_{i,j,k}
\]
复杂度是\(O(mn^3)\),可以获得\(84pts\)
其实我们并不关心\(j,k\)的具体数字,所以我们可以压缩成\(j-k\)这一个下标
但是考虑到会有负数,所以直接平移\(n\)
这样的复杂度是\(O(mn^2)\)的
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void read(T& x)
{
x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
#define maxn 105
#define P 998244353
long long f[maxn][maxn*2];
int n,m,c[maxn][maxn*20],sum[maxn];
long long ans=1;
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
read(c[i][j]),sum[i]=(1LL*sum[i]+c[i][j])%P;
for(int i=1;i<=n;++i) ans=ans*(sum[i]+1)%P;
for(int i=1;i<=m;++i)//col
{
memset(f,0,sizeof(f));
f[0][n]=1;
for(int j=1;j<=n;++j)//i
for(int k=n-j;k<=n+j;++k)//j-k
f[j][k]=(f[j-1][k]+1LL*f[j-1][k-1]*c[j][i]%P+1LL*f[j-1][k+1]*((sum[j]-c[j][i]+P)%P)%P)%P;
for(int j=1;j<=n;++j) ans=(ans-f[n][n+j]+P)%P;
}
printf("%lld\n",ans-1);
return 0;
}
一切伟大的行动和思想,都有一个微不足道的开始。
There is a negligible beginning in all great action and thought.
There is a negligible beginning in all great action and thought.