ARC114E - Paper Cutting 2
Solution
考场上时间不够,没刚出来QAQ。
做法和官解本质相同,只是官解运用期望的线性性直接导出答案,而这里是对于所有方案统计贡献在除以方案数,从期望的定义上计算答案。可能稍显复杂。
Part one
我们要求的是合法操作序列的期望长度。
问题大概可以用一个类似《PKUWC2018猎人杀》的经典的套路转化为:
设有一个
W
+
H
−
2
W+H-2
W+H−2条线组成的排列
p
1
p
2
.
.
.
p
n
p_1p_2...p_n
p1p2...pn,其中
p
i
p_i
pi的贡献为
1
1
1当且仅当不存在一个
p
j
(
j
<
i
)
p_j(j<i)
pj(j<i)可以把
p
i
p_i
pi叉掉,也就是说
p
i
p_i
pi前面没有一个能结束游戏或者把
p
i
p_i
pi的那一半白纸去掉的数,否则
p
i
p_i
pi贡献为
0
0
0,一个排列的贡献是所有数的贡献和。
不难看出每个排列都映射到一组合法操作序列(即贡献为 1 1 1的 p i p_i pi序列),每个排列的贡献对应一组合法方案的长度,根据期望的定义( ∑ c i p i \sum c_ip_i ∑cipi)容易证明上面所有排列的期望贡献和就是我们要求的合法方案的长度和。
Part two
因此我们想要求所有 W + H − 2 W+H-2 W+H−2条线的 ( W + H − 2 ) ! (W+H-2)! (W+H−2)!种排列的期望贡献,我们可以求出所有排列的贡献再除以方案数。
于是问题变成怎么求 ( W + H − 2 ) ! (W+H-2)! (W+H−2)!种排列的贡献和。
这里我们运用类似这场 A R C ARC ARC的 C C C题的方法,对于每个数,统计其对答案的贡献。也就是考虑这个数 x x x会在多少个排列里贡献为 1 1 1,即有多少个排列满足在 x x x之前不存在能叉掉 x x x的数。
设行的编号为 1 , 2 , . . . , H − 1 1,2,...,H-1 1,2,...,H−1列的编号为 1 , 2 , . . . , W − 1 1,2,...,W-1 1,2,...,W−1,不妨令 w 1 < w 2 , h 1 < h 2 w_1<w_2,h_1<h_2 w1<w2,h1<h2(显然这个顺序没有影响)。
先考虑列的贡献,分类讨论:
- 若 x < w 1 x < w_1 x<w1,则前面不能出现在 [ x , w 1 ) ∪ [ w 1 , w 2 ) [x,w_1)\cup[w_1,w_2) [x,w1)∪[w1,w2)中的数。
- 若 w 1 ≤ x < w 2 w_1 \leq x < w_2 w1≤x<w2,则前面不能出现 [ w 1 , w 2 ) [w_1,w_2) [w1,w2)中的数。
- 若 x > w 2 x > w_2 x>w2,则前面不能出现在 ( w 2 , x ] ∪ [ w 1 , w 2 ) (w_2,x]\cup[w_1,w_2) (w2,x]∪[w1,w2)中的数。
对于第一种情况,我们枚举
x
x
x,再枚举它在排列中的位置,贡献即为:
∑
i
=
1
w
1
−
1
∑
j
=
1
W
+
H
−
2
(
W
+
H
−
2
−
(
(
w
2
−
1
)
−
i
+
1
)
j
−
1
)
\sum_{i = 1}^{w_1-1}\sum_{j = 1} ^{W + H - 2}\binom{W+H-2-((w_2-1)-i+1)}{j - 1}
i=1∑w1−1j=1∑W+H−2(j−1W+H−2−((w2−1)−i+1))
用上指标求和化简得:
∑
j
=
1
W
+
H
−
2
(
W
+
H
−
2
−
w
2
+
w
1
j
)
−
(
W
+
H
−
2
−
w
2
+
1
j
)
\sum_{j = 1} ^{W + H - 2}\binom{W+H-2-w_2+w_1}{j}-\binom{W+H-2-w_2+1}{j}
j=1∑W+H−2(jW+H−2−w2+w1)−(jW+H−2−w2+1)
这样就可以
O
(
W
+
H
)
O(W+H)
O(W+H)计算了。
第二种和第三种是类似的,行的贡献也是类似的,这里就不再赘述了。
总时间复杂度 O ( W + H ) O(W+H) O(W+H)。
Code
实现上有一点点小细节。
//省略快读和头文件
int fac[MAXN], inv[MAXN];
inline int upd(int x, int y) { return x + y >= mods ? x + y - mods : x + y; }
inline int quick_pow(int x, int y) {
int ret = 1;
for (; y ; y >>= 1) {
if (y & 1) ret = 1ll * ret * x % mods;
x = 1ll * x * x % mods;
}
return ret;
}
inline int C(int x, int y) { return (x < y || y < 0) ? 0 : 1ll * fac[x] * inv[y] % mods * inv[x - y] % mods; }
void Init(int n) {
fac[0] = 1;
for (int i = 1; i <= n ; ++ i) fac[i] = 1ll * fac[i - 1] * i % mods;
inv[n] = quick_pow(fac[n], mods - 2);
for (int i = n - 1; i >= 0 ; -- i) inv[i] = 1ll * inv[i + 1] * (i + 1) % mods;
}
int solve(int n, int h1, int h2, int w1, int w2, int W) {
int ans = 0;
if (w1 > 1)
for (int i = 2; i <= n - h2 + h1 - w2 + w1 ; ++ i)
ans = upd(ans, 1ll * fac[n - i] * fac[i - 1] % mods * upd(C(n - h2 + h1 - w2 + w1, i), mods - C(n - h2 + h1 - w2 + 1, i)) % mods);
if (w1 < w2)
for (int i = 2; i <= n - h2 + h1 - w2 + w1 + 1; ++ i)
ans = upd(ans, 1ll * fac[n - i] * fac[i - 1] % mods * C(n - h2 + h1 - w2 + w1, i - 1) % mods * (w2 - w1) % mods);
if (w2 < W)
for (int i = 2; i <= n - h2 + h1 - w2 + w1 ; ++ i)
ans = upd(ans, 1ll * fac[n - i] * fac[i - 1] % mods * upd(C(n - h2 + h1 - w2 + w1, i), mods - C(n - h2 + h1 - W + w1, i)) % mods);
return upd(ans, 1ll * (W - 1) * fac[n - 1] % mods);
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("a.in", "r", stdin);
#endif
int H, W, h1, w1, h2, w2, n;
read(H), read(W), read(h1), read(w1), read(h2), read(w2), n = H + W - 2;
if (w1 > w2) swap(w1, w2);
if (h1 > h2) swap(h1, h2);
Init(n);
printf("%lld\n", 1ll * inv[n] * upd(solve(n, h1, h2, w1, w2, W), solve(n, w1, w2, h1, h2, H)) % mods);
return 0;
}