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;
}

posted @ 2020-01-20 17:02  DQY_dqy  阅读(162)  评论(0编辑  收藏  举报