\(\large\mathcal{Description}\)
有 \(n\) 个数对 \((A_i,A_j)\). 求:
\[\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n{a_i+b_i+a_j+b_j \choose a_i+a_j} \]答案对 \(10^9+7\) 取模.
\(\large\mathcal{Solution}\)
暴力求解上述式子是 \(\mathcal{O}(n^2)\) 的,我们考虑如何优化它。
给出引理:在平面直角坐标系中,点 \((x_1, y_1)\) 为起点, \((x_2,y_2)\) 为终点,每次只能往右或往上走(\(x_2\ge x_1, y_2\ge y_1\))。则方案数为:
\[y_2-x_2+y_1-x_1 \choose y_2-x_2 \]感性理解:从 \((x_1, y_1)\) 到 \((x_2,y_2)\) 要走 \(y_2-x_2+y_1-x_1\) 步,其中 \(y_2-x_2\) 步向上走。
由上述引理,得知组合数的几何意义: \(a_i+b_i+a_j+b_j \choose a_i+a_j\) 为从 \((-a_i, -b_i)\) 到 \((a_j, b_j)\) 的方案数。发现:起点只与 \(i\) 有关, 终点只与 \(j\) 有关。
令 \(f(i,\ j)\) 表示 \((i, j)\) 到所有 \((-a_x, -b_x)_{1\le x\le n}\) 路径条数之和。
\(f(i,\ j)\) 是容易用 \(\text{dp}\) 处理的,方法如下:
- 对于 \(\forall x\in\{1,2,\cdots,n\}\),令 \(f_{a_i,\ b_i} \leftarrow 1\)
- 对所有格子执行 \(f_{i, j} = f_{i, j - 1} + f_{i - 1, j}\).
答案自然就是 \(\sum\limits_{i=1}^nf(a_i,\ b_i)\) 再减去每个 \((a_i,b_i)\) 到 \((-a_i,-b_i)\) 路径条数之和。
即:
\[\sum\limits_{i=1}^nf(a_i,\ b_i)-\sum\limits_{i=1}^n {2a_i+2b_i \choose 2a_i} \]时间复杂度:\(\mathcal{O}(\max(a_i^2,\ b_i^2)\ +\ n)\)
\(\large\mathcal{Code}\)
#include <bits/stdc++.h>
#define reg register
using namespace std;
const int N = 200010, S = 2010;
const int mod = 1000000007;
int n, a[N], b[N];
int frac[N], inv[N], ifrac[N];
int f[2 * S + 10][2 * S + 10];
void pre()
{
frac[0] = inv[1] = ifrac[0] = 1;
for (reg int i = 1; i <= 4 * S; ++ i) frac[i] = 1ll * i * frac[i - 1] % mod;
for (reg int i = 2; i <= 4 * S; ++ i) inv[i] = (mod - 1ll * (mod / i) * inv[mod % i] % mod) % mod;
for (reg int i = 1; i <= 4 * S; ++ i) ifrac[i] = 1ll * inv[i] * ifrac[i - 1] % mod;
}
int C(int n, int m)
{
return 1ll * frac[n] * ifrac[m] % mod * ifrac[n - m] % mod;
}
int main()
{
scanf("%d", &n);
for (reg int i = 1; i <= n; ++ i) scanf("%d %d", &a[i], &b[i]);
pre();
for (reg int i = 1; i <= n; ++ i) f[S - a[i]][S - b[i]] ++ ;
for (reg int i = 1 - S; i <= S; ++ i)
for (reg int j = 1 - S; j <= S; ++ j)
f[i + S][j + S] = ((f[i + S][j + S] + f[i + S - 1][j + S]) % mod + f[i + S][j + S - 1]) % mod;
int ans = 0;
for (reg int i = 1; i <= n; ++ i)
{
ans = (ans + f[S + a[i]][S + b[i]]) % mod;
ans = (ans - C(2 * a[i] + 2 * b[i], 2 * a[i]) + mod) % mod;
}
printf("%d\n", 1ll * ans * inv[2] % mod);
return 0;
}