BBQ Hard
题面链接:Click here
Solution:
看到这个式子,直接算显然是不好算的,不妨考虑式子的组合意义
我们知道\({n+m \choose m}\)可以表示从(0,0)走到(n,m)只能向右和向上的方案数,那么\({a_i+b_i+a_j+b_j \choose a_i+a_j}\)也是同理
我们把她平移一下,那么就变成了从\((-a_i,-a_j)\)走到\((b_i,b_j)\)的方案数了
注意到值域范围很小,可以直接dp,\(f[i][j]\)表示从所有起点到\((i,j)\)的方案数
最后统计答案的时候,注意到我们多算了\((-a_i,-a_j)\,\,to \,\,(a_i,a_j)\),并且比\(i\)小的我们也统计了
那么我们要减去\((a_i+a_j)\times2 \choose a_i+a_j\),然后再除以2,才是我们的答案
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4e3+11;
const int M=2e5+11;
const int mod=1e9+7;
const int bs=2e3+1;
int n,k,f[N][N],a[M],b[M];
int ans,fac[(N<<1)+11],ifac[(N<<1)+11];
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int qpow(int a,int b){
int re=1;
while(b){
if(b&1) re=(re*a)%mod;
b>>=1;a=a*a%mod;
}return re;
}
int C(int a,int b){
if(a<b) return 0;
return fac[a]*ifac[b]%mod*ifac[a-b]%mod;
}
signed main(){
n=read();
for(int i=1;i<=n;i++)
a[i]=read(),b[i]=read();
fac[0]=1;ifac[0]=1;
for(int i=1;i<=2*N;i++) fac[i]=fac[i-1]*i%mod;
ifac[2*N]=qpow(fac[2*N],mod-2);
for(int i=2*N-1;i;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
for(int i=1;i<=n;i++) ++f[bs-a[i]][bs-b[i]];
for(int i=1;i<N;i++)
for(int j=1;j<N;j++)
f[i][j]=(f[i][j]+(f[i-1][j]+f[i][j-1])%mod)%mod;
for(int i=1;i<=n;i++){
ans=(ans+f[bs+a[i]][bs+b[i]])%mod;
ans=(ans+mod-C(2*(a[i]+b[i]),a[i]+a[i]))%mod;
}
printf("%lld\n",ans*ifac[2]%mod);
return 0;
}