[Keyence2019] Paper Cutting

发布时间 2024-01-13 16:47:30作者: Hanx16Msgr

Paper Cutting

Luogu AT_keyence2019_f

题面翻译

有一个 \((H+1)\times(W+1)\) 的网格,网格中有 \(H\) 条水平线和 \(W\) 条竖直线。

你需要执行 \(K\) 次操作,每次沿一条水平线或竖直线将网格切开。定义一次操作的权值为切割后网格被切分的块数。

定义一个操作序列的权值为 \(K\) 次操作的权值和。

求所有操作序列的权值之和,答案对 \(10^9+7\) 取模。

其中 \(1\leq H,W\leq 10^7\)\(1\leq K \leq H+W\),且 \(H,W,K\) 为整数。

Solution

考虑单次操作的贡献是什么,假设当前为第 \(t\) 次操作。考虑第 \(t\) 次操作为当前操作的操作序列个数,容易算出为:

\[t!(k-t)!\binom{H+W-t}{K-t} \]

而这次操作的贡献为:

\[\sum\limits_{i=0}^t\binom{H}{i}\binom{W}{t-i}(i+1)(t-i+1) \]

那么贡献即为:

\[t!(K-t)!\binom{H+W-t}{K-t}\sum\limits_{i=0}^t\binom{H}{i}\binom{W}{t-i}(i+1)(t-i+1) \]

序列个数不难 \(\mathcal O(n)-\mathcal O(1)\) 计算,而贡献为 \(\mathcal O(n)\) 计算,考虑优化。

化式子:

\[\begin{array}{rll} &&\displaystyle\sum\limits_{i=0}^t\binom{H}{i}\binom{W}{t-i}(i+1)(t-i+1)\\ &=&\displaystyle(t+1)\left(\sum\limits_{i=0}^t\binom{H}{i}\binom{W}{t-i}\right)+i(t-i)\left(\sum\limits_{i=0}^t\binom{H}{i}\binom{W}{t-i}\right)\\ \end{array} \]

前半部分直接使用范德蒙德卷积,后半部分拆一下组合数:

\[\begin{array}{rll} &=&\displaystyle(t+1)\binom{H+W}{t}+\sum\limits_{i=0}^t\dfrac{H!W!}{(i-1)!(H-i)!(t-i-1)!(W-t+1)!}\\ &=&\displaystyle(t+1)\binom{H+W}{t}+\sum\limits_{i=0}^t\dfrac{HW(H-1)!(W-1)!}{(i-1)!(H-i)!(t-i-1)!(W-t+1)!}\\ &=&\displaystyle(t+1)\binom{H+W}{t}+HW\sum\limits_{i=0}^t\binom{H-1}{i-1}\binom{W-1}{t-i-1} \end{array} \]

同样使用范德蒙德卷积可以得到:

\[\begin{array}{rll} &=&\displaystyle(t+1)\binom{H+W}{t}+HW\binom{H+W-2}{t-2} \end{array} \]

显然可以 \(\mathcal O(n)-\mathcal O(1)\) 计算。

时间复杂度 \(\mathcal O(n)\)

Code
int N, M, K;
mint fac[_N], ifac[_N];
void init(int n) {
    fac[0] = 1; For(i, 1, n) fac[i] = fac[i-1] * i;
    ifac[n] = 1 / fac[n]; Rof(i, n, 1) ifac[i-1] = ifac[i] * i;
}
mint binom(int x, int y) {
    if (x < y || y < 0) return 0;
    return fac[x] * ifac[y] * ifac[x-y];
}
void _() {
    cin >> N >> M >> K; init(N + M);
    mint ans = 0;
    For(k, 1, K) {
        mint tmp = fac[k] * fac[K - k] * binom(N + M - k, K - k);
        tmp *= (k + 1) * binom(N + M, k) + binom(N + M - 2, k - 2) * N * M;
        ans += tmp;
    }
    cout << ans << '\n';
}