1. 程式人生 > >【AtCoder1983】BBQ Hard (組合數+巧妙模型轉化)

【AtCoder1983】BBQ Hard (組合數+巧妙模型轉化)

半題解

輸入A[i],B[i],求i=1Nj=i+1NCAi+Aj+Bi+BjAi+Bi\sum_{i=1}^N\sum_{j=i+1}^N C_{A_i+A_j+B_i+B_j}^{A_i+B_i}

題解

CAi+Aj+Bi+BjAi+BiC_{A_i+A_j+B_i+B_j}^{A_i+B_i} 上式即求從(Ai,Bi)(-A_i,-B_i)(Aj,Bj)(A_j,B_j)的路徑條數。 整個求和式即求從若干點出發,到任意終點的路徑條數。 可用DP在O

(n2)O(n^2)內解決

dp[i][j]dp[i][j]表示從到(i,j)(i,j)的方案數 dp[i][j]=dp[i1][j]+dp[i][j1]dp[i][j]=dp[i-1][j]+dp[i][j-1] 初始時 dp[Ai][Bi]=1dp[-A_i][-B_i]=1 結果為dp[Aj][Bj]\sum dp[A_j][B_j]

最後結果減去從(Ai,Bi)(-A_i,-B_i)(Ai,Bi)

(A_i,B_i)的方案數,再除以2。(i到j和j到i都被計算了一次)

程式碼

#include<cstdio>
const int MAXN=200005,MAXA=8010,ZERO=2003,MOD=1000000007;

int PowMod(int a,int b)
{
    int ret=1;
    while(b)
    {
        if(b&1)
            ret=1LL*ret*a%MOD;
        a=1LL*a*a%MOD;
        b>>=1;
    }
    return ret;
}

int fac[
MAXA],ifac[MAXA]; void Init() { fac[0]=1; for(int i=1;i<MAXA;i++) fac[i]=1LL*fac[i-1]*i%MOD; ifac[MAXA-1]=PowMod(fac[MAXA-1],MOD-2); for(int i=MAXA-2;i>=0;i--) ifac[i]=1LL*ifac[i+1]*(i+1)%MOD; } int N; int A[MAXN],B[MAXN]; int dp[MAXA/2][MAXA/2]; int main() { Init(); scanf("%d",&N); for(int i=1;i<=N;i++) { scanf("%d%d",&A[i],&B[i]); dp[ZERO-A[i]][ZERO-B[i]]++; } for(int i=1;i<MAXA/2;i++) for(int j=1;j<MAXA/2;j++) dp[i][j]=((dp[i][j]+dp[i][j-1])%MOD+dp[i-1][j])%MOD; int ans=0; for(int i=1;i<=N;i++) ans=(ans+dp[ZERO+A[i]][ZERO+B[i]])%MOD; for(int i=1;i<=N;i++) ans=(ans-1LL*fac[2*(A[i]+B[i])]*ifac[2*A[i]]%MOD*ifac[2*B[i]]%MOD)%MOD; ans=(ans+MOD)%MOD; ans=1LL*ans*PowMod(2,MOD-2)%MOD; printf("%d\n",ans); return 0; }