[ABC297F] Minimum Bounding Box 2

也许更好的阅读体验

D e s c r i p t i o n \mathcal{Description} Description
在一个 H H H W W W 列的网格图上随机选择 K K K 个点。定义当前局面的分数为最小的可以围住这 K K K 个点的矩形的面积。

请求出所有局面中分数的期望值,输出时对 998244353 998244353 998244353取模。

S o l u t i o n \mathcal{Solution} Solution
先说经典的容斥方法,考虑围住这 k k k个点的矩形的大小为 n ∗ m n *m nm
k k k个点全部落在这个矩形范围内的方案数为 ( n m k ) \begin{pmatrix}nm\\ k\end{pmatrix} (nmk)
接下来考虑围住 k k k个点的最小矩形并不是 n ∗ m n *m nm的方案数
而这种情况下,一定有一条边上没有点
容斥的方法就出来了,每次考虑几条边上没有点然后考虑一下是什么样的情况,如何加减即可

另一种方法是直接推
f [ i ] [ j ] f[i][j] f[i][j]表示围住 k k k个点的矩形大小为 i ∗ j i *j ij的方案数
则很容易有 f [ i ] [ j ] = ( i j k ) − ∑ p = 1 i ∑ q = 1 j ( i − p + 1 ) ( j − q + 1 ) f [ p ] [ q ] f[i][j] = \begin{pmatrix}ij\\ k\end{pmatrix}-\sum\limits_{p = 1}^{i}\sum\limits_{q=1}^{j}(i-p+1)(j-q+1)f[p][q] f[i][j]=(ijk)p=1iq=1j(ip+1)(jq+1)f[p][q]
复杂度是 O ( n 4 ) O(n^4) O(n4)的,对这个式子变形
f [ i ] [ j ] = ( i j k ) − ∑ p = 1 i ∑ q = 1 j [ ( i + 1 ) ( j + 1 ) − ( j + 1 ) p − ( i + 1 ) q + p q ] f [ p ] [ q ] f[i][j] = \begin{pmatrix}ij\\ k\end{pmatrix}-\sum\limits_{p = 1}^{i}\sum\limits_{q=1}^{j}[(i+1)(j+1)-(j+1)p-(i+1)q+pq]f[p][q] f[i][j]=(ijk)p=1iq=1j[(i+1)(j+1)(j+1)p(i+1)q+pq]f[p][q]
其中 i , j i,j i,j都可看为常量,我们只需维护这几个前缀和就可以方便的转移了: i f [ i ] [ j ] ,   j f [ i ] [ j ]   , f [ i ] [ j ] ,   i j f [ i ] [ j ] if[i][j],\ jf[i][j]\ ,f[i][j],\ ijf[i][j] if[i][j], jf[i][j] ,f[i][j], ijf[i][j]

C o d e \mathcal{Code} Code

#include <iostream>
using namespace std;
const int mod = 998244353;
const int maxn = 1003;
const int maxm = 1000006;
int n, m, k, ans;
int f[maxn][maxn], s[maxn][maxn], si[maxn][maxn], sj[maxn][maxn], sij[maxn][maxn];
int fac[maxm], ifac[maxm];
void add (int &x, int y){ x = ((x + y) % mod + mod) % mod; }
int C (int n, int m)
{
    if (n < m)  return 0;
    return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
int main ()
{
    cin >> n >> m >> k;
    fac[0] = ifac[0] = ifac[1] = 1;
    for (int i = 1; i <= 1e6; ++i)  fac[i] = 1ll * fac[i - 1] * i % mod;
    for (int i = 2; i <= 1e6; ++i)  ifac[i] = (mod - 1ll * mod / i * ifac[mod % i] % mod) % mod;
    for (int i = 1; i <= 1e6; ++i)  ifac[i] = 1ll * ifac[i - 1] * ifac[i] % mod;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j) {
            f[i][j] = C(i * j, k);
            add(s[i][j], ((s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1]) % mod + mod) % mod);
            add(si[i][j], ((si[i - 1][j] + si[i][j - 1] - si[i - 1][j - 1]) % mod + mod) % mod);
            add(sj[i][j], ((sj[i - 1][j] + sj[i][j - 1] - sj[i - 1][j - 1]) % mod + mod) % mod);
            add(sij[i][j], ((sij[i - 1][j] + sij[i][j - 1] - sij[i - 1][j - 1]) % mod + mod) % mod);

            add(f[i][j], -1ll * (i + 1) * (j + 1) * s[i][j] % mod);
            add(f[i][j], 1ll * (i + 1) * sj[i][j] % mod);
            add(f[i][j], 1ll * (j + 1) * si[i][j] % mod);
            add(f[i][j], -sij[i][j]);

            add(s[i][j], f[i][j]);
            add(si[i][j], 1ll * i * f[i][j] % mod);
            add(sj[i][j], 1ll * j * f[i][j] % mod);
            add(sij[i][j], 1ll * i * j * f[i][j] % mod);

            add(ans, 1ll * f[i][j] * i % mod * j % mod * (n - i + 1) % mod * (m - j + 1) % mod);
        }
    cout << 1ll * ans * ifac[n * m] % mod * fac[k] % mod * fac[n * m - k] % mod << endl;
    return 0;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值