组合意义简单题。
注意这个组合数的形式,我们显然可以转化为从 $(0,0$ 走到 $(n,m)$ 的方案数。$n$ 范围较大,但 $a_i,b_i$ 值域范围较小,我们考虑继续沿用这个思路。相当于求所有 $(-a_i,-b_i)$ 走到 $(a_j,b_j)$ 的方案数。我们可以 $\mathcal O(V^2)$ DP 求出,再用 $\mathcal O(n)$ 统计。注意需要减去 $i=j$ 的答案。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include<iostream> #include<cstdio> using namespace std; const int N=2e5+10,M=2e3+10,mod=1e9+7; template<class T> inline void read(T &x) { x=0;bool f=0; 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^48),ch=getchar(); if(f)x=~x+1; } template<class T,class ...T1> inline void read(T &x,T1 &...x1) { read(x),read(x1...); } int n; int a[N],b[N]; int fac[N],ifac[N]; int f[M<<1][M<<1]; inline int adj(int x){return x>=mod?x-mod:x;} inline int qpow(int x,int k=mod-2) { int res=1; while(k) { if(k&1)res=1ll*res*x%mod; x=1ll*x*x%mod; k>>=1; } return res; } inline void init(int n) { fac[0]=1; for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod; ifac[n]=qpow(fac[n]); for(int i=n;i;i--)ifac[i-1]=1ll*ifac[i]*i%mod; } inline int C(int n,int m) { if(m>n)return 0; return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod; } int main() { read(n); for(int i=1;i<=n;i++)read(a[i],b[i]),f[M-a[i]][M-b[i]]++; for(int i=1;i<M<<1;i++) for(int j=1;j<M<<1;j++) f[i][j]=adj(f[i][j]+adj(f[i-1][j]+f[i][j-1])); init(M<<2); int ans=0; for(int i=1;i<=n;i++)ans=adj(adj(ans+f[M+a[i]][M+b[i]])+mod-C(a[i]+b[i]<<1,a[i]<<1)); ans=1ll*ans*qpow(2)%mod; printf("%d",ans); return 0; }
|