# NOIP2019_Day2T1_Emiya 家今天的饭

题意:


原题意:

Emiya 是个擅长做菜的高中生,他共掌握 \(n\) 种烹饪方法,且会使用 \(m\) 种主要食材做菜。为了方便叙述,我们对烹饪方法从 \(1∼n\) 编号,对主要食材从 \(1∼m\) 编号。

Emiya 做的每道菜都将使用恰好一种烹饪方法与恰好一种主要食材。更具体地,Emiya 会做 \(a_{i,j}\) 道不同的使用烹饪方法 \(i\) 和主要食材 \(j\) 的菜 \((1≤i≤n,1≤j≤m)\)

这也意味着 Emiya 总共会做 \(\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m} a_{i,j}\) 道不同的菜。

Emiya 今天要准备一桌饭招待 Yazid 和 Rin 这对好朋友,然而三个人对菜的搭配有不同的要求,更具体地,对于一种包含 \(k\) 道菜的搭配方案而言:

  • Emiya 不会让大家饿肚子,所以将做至少一道菜,即 \(k≥1\)

  • Rin 希望品尝不同烹饪方法做出的菜,因此她要求每道菜的烹饪方法互不相同

  • Yazid 不希望品尝太多同一食材做出的菜,因此他要求每种主要食材至多在一半的菜(即 \(\lfloor \frac{k}{2} \rfloor\) 道菜)中被使用

这里的 \(\lfloor x \rfloor\) 为下取整函数,表示不超过 \(x\) 的最大整数。

这些要求难不倒 Emiya,但他想知道共有多少种不同的符合要求的搭配方案。两种方案不同,当且仅当存在至少一道菜在一种方案中出现,而不在另一种方案中出现。

Emiya 找到了你,请你帮他计算,你只需要告诉他符合所有要求的搭配方案数对质数 998,244,353 取模的结果。


抽象题意:

给出一个 \(n*m\) 矩阵,要求从中选出 \(k\) 个格子,\(k≥1\)

每行能选一个格子或者不选,每列上选的格子总数不能超过 \(k\) 的一半,

同时每个格子内也有方案 \(a_{i,j}\) 种,求满足所有要求的总方案数模998,244,353

解:


\[\LARGE ---咕咕咕咕咕!--- \]

题解又被我咕掉了——援引Misaka Mikoto巨佬的一篇题解吧

代码:


题解中巨佬用了很聪明的方法处理下表为负的情况——添加hash函数

代码的实现方面进一步进行了空间优化——观察转移方程发现随着 \(i,\ k\) 不断枚举,\(j\) 始终是不变的!

并且 \(f_{...,\ j,\ ...}\) 的更新不会由 \(f_{...,\ j-1,\ ...}\) 的某个状态转移来

这就好办了,我们只用记 \(f_{i,\ k}\) ,外层的 \(j\) 让它不断枚举,并把它看作不变的量

但是要注意,从计算完一个 \(j\) 该计算 \(j+1\) 的时候,f数组要清空

还有初始化:

ans最开始初始化为所有方案数,记得sum[i]还要加一,对应不选的情况,ans = (ans * (sum[i] + 1)) % mod;

\(f[1][0]=1\) —— 从前 \(1\) 行选格子,使得第 \(j\) 种食材数量减其他食材数量为0 —— 唯一的办法是第一行什么也不选

\(f[1][1]=a[1][j]\) —— 从前 \(1\) 行选格子,使得第 \(j\) 种食材数量减其他食材数量为1 —— 只能是选上第 \(j\) 种食材的一道菜,其中有 \(a[1][j]\) 种选法

\(f[1][-1]=\sum_{j'=1\ \&\&\ j' \ne j}^{m}a[1][j']\) —— 从前 \(1\) 行选格子,使得第 \(j\) 种食材数量减其他食材数量为-1 —— 办法是选除了 \(j\) 种食材的任意一种,方法加起来即可

至此,第一行的情况被考虑完全,\(i\) 从2开始枚举

算完一个 \(j\) 之后,注意如何统计答案——答案是由ans-所有不符合条件的情况数

一个情况不符合条件当且仅当选 \(j\) 种食材比选其他所有的食材数量多1~n,对应选择 \(j\) 种食材 \(\lfloor \frac{k}{2} \rfloor+1 ∼ n\)

(还要卡卡常)

1、写个快读

2、取模的偷工减料,考虑到 \(f[i-1][h(k)]+a[i][j]*f[i-1][h(k-1)]+f[i-1][h(k+1)]*(sum[i]+mod-a[i][j])\) 每一项最大都为998244353-1,

所以这个式子最大值大概为1,992,983,578,589,265,924,比ll的最大范围小,不会溢出,所以中间的取模都省了,最后只用模一次

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
int n, m;
ll a[110][2010];
ll sum[110];
ll f[110][2010];
ll ans;

ll read() {
    ll xx = 0; char ch = getchar();
    while (ch < '0'|| ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') xx = (xx << 1) + (xx << 3) + ch - '0', ch = getchar();
    return xx;
}
inline int h(int x) { return x + n + 5; }

int 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] = (sum[i] + a[i][j]) % mod;
        }
    ans = 1;
    for (int i = 1; i <= n; i++)
        ans = (ans * (sum[i] + 1)) % mod;
    ans--;

    for (int j = 1; j <= m; j++) {
        memset(f, 0, sizeof f);
        f[1][h(0)] = 1;
        f[1][h(1)] = a[1][j];
        f[1][h(-1)] = (sum[1] + mod - a[1][j]) % mod;

        for (int i = 2; i <= n; i++) 
            for (int k = -n; k <= n; k++)
                f[i][h(k)] = ( (f[i - 1][h(k)] + a[i][j] * f[i - 1][h(k - 1)])
                + (f[i - 1][h(k + 1)] * (sum[i] + mod - a[i][j])) ) % mod;

        for (int k = 1; k <= n; k++)
            ans = (ans + mod - f[n][h(k)]) % mod;
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2020-07-31 21:35  熹圜  阅读(146)  评论(0编辑  收藏  举报