AGC001E BBQ Hard

Description

你有\(n\)个串,第\(i\)个串上有\(A_i\)块肉和\(B_i\)块菜。现在选出两个串,将他们中的肉和菜放在一起重新排列,求总方案数。

\(2\leq n\leq 200000, 1\leq A_i,B_i\leq 2000\)

Solution

很显然对于两个串\(i, j\),对答案的贡献是: \[ \binom{A_i + B_i + A_j + B_j}{A_i + A_j} \] 这个东西有什么组合意义吗?答案是有的。就是在一个平面上从原点走到\((A_i + A_j, B_i + B_j)\),每次只向上或者向右走一格的方案数,然后我们把整个坐标系平移一下就可以把这个东西变成从\((-A_i, -B_i)\)走到\((A_j, B_j)\)

然后我们建出所有的点,就是要我们求所有正点走到所有负点的方案数之和。这个东西很好DP,我们先把横纵坐标都加2000,然后根据到\((0, 0)\)的曼哈顿距离从小到大递推即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <utility>
using pii = std::pair<int, int>;
const int ha = 1000000007;
using ll = long long;
ll pow_mod(ll a, ll b) {
ll ans = 1, res = a;
while(b) {
if(1LL & b) ans = ans * res % (ll)ha;
res = res * res % (ll)ha; b >>= 1;
}
return ans;
}
ll inv(ll x) {
return pow_mod(x, ha - 2);
}

ll fac[10005], ifac[10005];
void process() {
int n = 10000;
fac[0] = 1;
for(int i = 1; i <= n; i ++) {
fac[i] = (fac[i - 1] * (ll(i))) % (ll)ha;
}
ifac[n] = inv(fac[n]);
for(int i = n - 1; i >= 0; i --) {
ifac[i] = ifac[i + 1] * (ll(i + 1)) % (ll)ha;
}
}
ll C(int x, int y) {
ll ret = fac[x];
ret = ret * ifac[x - y] % (ll)ha;
ret = ret * ifac[y] % (ll)ha;
return ret;
}

pii P[200005]; int d[4005][4005];
int n;
int dp() {
#ifdef LOCAL
puts("11111");
fflush(stdout);
#endif
for(int i = 1; i <= n; i ++) {
int x = P[i].first, y = P[i].second;
d[2000 - x][2000 - y] ++;
}
for(int dis = 1; dis <= 8000; dis ++) {
for(int i = std::max(0, dis - 4000); i <= std::min(4000, dis); i ++) {
int j = dis - i;
if(i > 0) d[i][j] = (d[i][j] + d[i - 1][j]) % ha;
if(j > 0) d[i][j] = (d[i][j] + d[i][j - 1]) % ha;
}
}
#ifdef LOCAL
puts("Gou!"); fflush(stdout);
#endif
int ans = 0;
for(int i = 1; i <= n; i ++) {
int x = P[i].first, y = P[i].second;
ans = (ans + d[x + 2000][y + 2000]) % ha;
ans = (ans - C(2 * x + 2 * y, 2 * x) + ha) % ha;
}
static const ll inv_2 = 500000004;
ans = (((ll(ans)) * inv_2) % (ll)ha);
return ans;
}

int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
scanf("%d%d", &P[i].first, &P[i].second);
}
process();
printf("%d\n", dp());
return 0;
}