转载注明来源: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 \not =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\) 意义下的逆元。
\(\text{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\n",ans*inv[2]%mod);
return 0;
}