转载注明来源:https://www.cnblogs.com/syc233/p/13627377.html
这题的模型转化挺巧妙的,不过也都是套路。
套路:从棋盘的 ((0,0)) 走到 ((n,m)) ,每步只能向上或向右走的方案数为 ({n+m choose n}) 。
考虑一个DP:令 (f_{i,j}) 表示从 ((0,0)) 走到 ((i,j)) 的方案数,有转移方程:
[f_{i,j}=f_{i-1,j}+f_{i,j-1}
]
尝试用组合数替换一下,得:
[{i+j choose i}={i+j-1 choose i-1}+{i+j+1 choose i}
]
这恰好是杨辉三角,说明 (f_{i,j}={i+j choose i}) 。
回到这道题。
由上面的套路,不难想出一个解法:枚举 (i,j) ,求出 ((0,0)) 走到 ((a_i+a_j,b_i+b_j)) 的方案数。
然而这和暴力没什么两样,都是 (O(n^2)) 级别的,因为终点与 (i,j) 都有关。
尝试平移下棋盘,则变成了求 ((-a_i,-b_i)) 走到 ((a_j,b_j)) 的方案数。
这样就好统计了。将每一个 (f_{-a_i,-b_i}) 赋初值 (1) ,DP统计答案即可。
因为题目中 (i ot =j) ,还要减去 (i=j) 的情况,即从 ((-a_i,-b_i)) 走到 ((a_i,b_i)) 的方案数,即 ({2a_i+2b_i choose 2a_i}) 。
因为每一对 ((i,j)) 只算一次,所以最后答案还要除以 (2) 。
因为是模意义下除以 (2) ,所以应该乘上 (2) 模 (10^9+7) 意义下的逆元。
( ext{Code}:)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 200005
#define maxm 2001
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const lxl mod=1e9+7;
template <typename T>
inline void read(T &x)
{
x=0;T f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
x*=f;
}
int N,A[maxn],B[maxn];
lxl f[(maxm<<1)+5][(maxm<<1)+5],ans;
lxl inv[(maxm<<2)+5],fac[(maxm<<2)+5],inv2[(maxm<<2)+5];
inline void init()
{
inv[0]=inv[1]=fac[0]=fac[1]=inv2[0]=inv2[1]=1;
for(int i=2;i<=(maxm<<2);++i)
{
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
fac[i]=fac[i-1]*i%mod;
inv2[i]=inv2[i-1]*inv[i]%mod;
}
}
inline lxl C(int n,int m)
{
return fac[n]*inv2[m]%mod*inv2[n-m]%mod;
}
int main()
{
// freopen("AT1983.in","r",stdin);
read(N);
init();
for(int i=1;i<=N;++i)
{
read(A[i]),read(B[i]);
++f[maxm-A[i]][maxm-B[i]];
}
for(int i=1;i<=(maxm<<1);++i)
for(int j=1;j<=(maxm<<1);++j)
(f[i][j]+=(f[i-1][j]+f[i][j-1])%mod)%=mod;
for(int i=1;i<=N;++i)
{
(ans+=f[maxm+A[i]][maxm+B[i]])%=mod;
(ans+=mod-C(2*A[i]+2*B[i],2*A[i]))%=mod;
}
printf("%lld
",ans*inv[2]%mod);
return 0;
}