AGC001 E - BBQ Hard【dp+组合数学】

首先直接按要求列出式子是\( \sum_{i=1}^{n}\sum_{j=i+1}^{n}C_{a_i+a_j+b_i+b_j}^{a_i+a_j} \)
这样显然过不了,因为ab的数据范围比较小,所以从这里入手,注意到后面的组合数含义是从点(ai,bi)走到点(-aj,-bj)的方案数
把但是j从i+1开始不好做,就容斥一下,把式子变成\( \sum_{i=1}^{n}\sum_{j=1}^{n}C_{a_i+a_j+b_i+b_j}^{a_i+a_j}-\sum_{i=1}^{n}C_{a_i+a_i+b_i+b_i}^{a_i+a_j} \)
所以设f[i][j]为从点(i,j)左下方存在的点走到点(i,j)的方案数总和,转移是f[i][j]+=f[i-1][j]+f[i][j-1]也就是从左点和下点走一步过来再加上这个点本来就存在的,就是原来的(ai,bi)要变成(-ai,-bi)
然后dp完对每个点都加到答案里再减掉后面的部分即可

#include<iostream>
#include<cstdio>
using namespace std;
const int N=200005,mod=1e9+7;
int n,a[N],b[N],f[4005][4005],fac[N],inv[N],ans;
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>'9'||p<'0')
    {
        if(p=='-')
            f=-1;
        p=getchar();
    }
    while(p>='0'&&p<='9')
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
int ksm(int a,int b)
{
    int r=1;
    while(b)
    {
        if(b&1)
            r=1ll*r*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return r;
}
int C(int n,int m)
{
    return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
    fac[0]=inv[0]=1;
    for(int i=1;i<=10000;i++)
        fac[i]=1ll*fac[i-1]*i%mod;
    inv[10000]=ksm(fac[10000],mod-2);
    for(int i=9999;i>=1;i--)
        inv[i]=1ll*inv[i+1]*(i+1)%mod;
    n=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read(),b[i]=read();
        f[2001-a[i]][2001-b[i]]++;
        ans=(ans-C(a[i]*2+b[i]*2,a[i]*2))%mod;
    }
    for(int i=1;i<=4002;i++)
        for(int j=1;j<=4002;j++)
            f[i][j]=(f[i][j]+f[i-1][j]+f[i][j-1])%mod;
    for(int i=1;i<=n;i++)
        ans=(ans+f[2001+a[i]][2001+b[i]])%mod;
    printf("%d\n",(1ll*ans*ksm(2,mod-2)%mod+mod)%mod);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lokiii/p/10922782.html