P5664 [CSP-S2019] Emiya 家今天的饭

n行m列的矩阵 ai  选 k 个数 每行选<=1 每列<=[k/2] 问有多少种方法

》dp 但是dp过程难以控制 “每行选<=1 每列<=[k/2]” 尤其是 每列<=[k/2] -> 所以考虑容斥

》不合法的行只需要 每次取<=1 列的话 最多只有一列因为<=k/2->枚举不合法的列

> g[][]->前i(1~n)行 选 j(0~n)个数字 的 所有合法的方案  

  1.选/不选->g[i][j]=max(g[i-1][j],g[i-1][j-1]+sum[i])

  g[0][0]=1

> f[i][j][k] 枚举col行不合法 前i(1~n)行 col 列选j 个 其他选k

  选(col、其他)、不选 ->f[i][j][k]=max(f[i-1][j][k],a[col]*f[i][j-1][k]+(sum[i]-a[col])*f[i-1][j][k-1])

  O(mn^3)

>优化 [j][k] 相对关系 ->f[i][j]前i行 col列 比其他列 多 j(-n~n)个数 O(mn^2)

  f[i][j] = (f[i-1][j]+f[i-1][j-1]*a[i][col]%mod+f[i-1][j+1]*sum[i][col]%mod)%mod;

  f[0][n]=1

 

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define mod 998244353

using namespace std;
typedef long long ll;
const int MAXN = 105, MAXM = 2005;
int n,m,a[MAXN][MAXM],sum[MAXN][MAXM];
ll f[MAXN][MAXN*2],g[MAXN][MAXN];

int main()
{
    cin >> n >> m;
    for(int i = 1; i<=n; i++)
        for(int j = 1; j<=m; j++)
        {
            scanf("%d",&a[i][j]);
            sum[i][0] = (sum[i][0]+a[i][j])%mod;
        }
    for(int i = 1; i<=n; i++)
        for(int j = 1; j<=m; j++)
            sum[i][j] = (sum[i][0]-a[i][j]+mod)%mod;
    ll ans = 0;
    for(int col = 1; col<=m; col++)
    {
        memset(f,0,sizeof(f));
        f[0][n] = 1;
        for(int i = 1; i<=n; i++)
            for(int j = n-i; j<=n+i; j++) 
                f[i][j] = (f[i-1][j]+f[i-1][j-1]*a[i][col]%mod+f[i-1][j+1]*sum[i][col]%mod)%mod;
        for(int j = 1; j<=n; j++)
            ans = (ans+f[n][n+j])%mod;
    }
    g[0][0] = 1;
    for(int i = 1; i<=n; i++)
        for(int j = 0; j<=n; j++) 
            g[i][j] = (g[i-1][j]+(j>0?g[i-1][j-1]*sum[i][0]%mod:0))%mod;
    for(int j = 1; j<=n; j++)
        ans = (ans-g[n][j]+mod)%mod;  
    cout << ans*(mod-1)%mod << endl;
    return 0;
}

 

posted @ 2023-09-09 15:45  JMXZ  阅读(26)  评论(0)    收藏  举报