题目大概要求的就是 ∑i=1n∑j=i+1nC(ai+aj+bi+bj,ai+aj) ∑ i = 1 n ∑ j = i + 1 n C ( a i + a j + b i + b j , a i + a j )
看这个式子发现怎么都不是很好求 观察到坐标范围不是很大 考虑组合意义
如果只能向右和向上走 并且 x<p,y<q x < p , y < q
则从点 (x,y) ( x , y ) 到点 (p,q) ( p , q ) 的方案数为 C(p−x+q−y,p−x) C ( p − x + q − y , p − x )
这个理解起来不是很难 就是总步数选取多少步向右走
回到题目 先把式子化一下 就变成了
(∑i=1n∑j≠inC(ai+aj+bi+bj,ai+aj))/2 ( ∑ i = 1 n ∑ j ≠ i n C ( a i + a j + b i + b j , a i + a j ) ) / 2
那么题目要求的式子 就可以转化成点 (−aj,−bj) ( − a j , − b j ) 到点 (ai,bi) ( a i , b i ) 的路径数
这个东西一个 O(坐标2) O ( 坐 标 2 ) 的 dp d p 就能解决 然后就做完了
当然对于每个点要记得减去 (−ai,bi) ( − a i , b i ) 到 (ai,bi) ( a i , b i ) 的方案数
Codes
#include<bits/stdc++.h>
using namespace std;
const int M = 2e5 + 10, N = 8000 + 10, mod = 1e9 + 7;
int a[M], b[M], fac[N], inv[N];
int ans, n, f[N][N];
int qpow(int a, int x) {
int ret = 1;
while(x) {
if(x & 1)
ret = 1ll * ret * a % mod;
x >>= 1, a = 1ll * a * a % mod;
}
return ret;
}
void Init(int maxn) {
scanf("%d", &n);
fac[0] = inv[0] = 1;
for(int i = 1; i <= maxn; ++ i)
fac[i] = 1ll * fac[i - 1] * i % mod;
inv[maxn] = qpow(fac[maxn], mod - 2);
for(int i = maxn; i >= 1; -- i)
inv[i - 1] = 1ll * inv[i] * i % mod;
}
int C(int n, int m) {
if(n < m) return 0;
return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("AT1983.in", "r", stdin);
freopen("AT1983.out", "w", stdout);
#endif
Init(N - 5);
for(int i = 1; i <= n; ++ i) {
scanf("%d%d", &a[i], &b[i]);
++ f[2002 - a[i]][2002 - b[i]];
}
for(int i = 1; i <= 4002; ++ i)
for(int j = 1; j <= 4002; ++ j)
(f[i][j] += (f[i - 1][j] + f[i][j - 1]) % mod) %= mod;
for(int i = 1; i <= n; ++ i)
(ans += f[a[i] + 2002][b[i] + 2002] - C((a[i] + b[i]) * 2, a[i] * 2)) %= mod;
cout << 1ll * (mod + 1) / 2 * (ans + mod) % mod << endl;
return 0;
}