123789456ye

已AFO

[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;
}
posted @ 2020-04-20 17:08  123789456ye  阅读(97)  评论(0编辑  收藏  举报