AoCoder 1983 [AGC001E] BBQ Hard(组合数+dp)

problem

洛谷链接

solution

∑ i = 1 n ∑ j = i + 1 n ( a i + b i + a j + b j a i + a j ) = ∑ i = 1 n ∑ j = 1 n ( a i + b i + a j + b j a i + a j ) − ∑ i = 1 n ( 2 ( a i + b i ) 2 a i ) 2 \sum_{i=1}^{n}\sum_{j=i+1}^n\binom{a_i+b_i+a_j+b_j}{a_i+a_j}=\frac{\sum_{i=1}^{n}\sum_{j=1}^n\binom{a_i+b_i+a_j+b_j}{a_i+a_j}-\sum_{i=1}^{n}\binom{2(a_i+b_i)}{2a_i}}{2} i=1nj=i+1n(ai+ajai+bi+aj+bj)=2i=1nj=1n(ai+ajai+bi+aj+bj)i=1n(2ai2(ai+bi))

C x + y x C_{x+y}^x Cx+yx 可以视为网格图中 ( 0 , 0 ) → ( x , y ) (0,0)\rightarrow (x,y) (0,0)(x,y) 的路径方案数

C a i + b i + a j + b j a i + a j = C ( a i + b i ) − ( − a j , − b j ) a i − ( − a j ) C_{a_i+b_i+a_j+b_j}^{a_i+a_j}=C_{(a_i+b_i)-(-a_j,-b_j)}^{a_i-(-a_j)} Cai+bi+aj+bjai+aj=C(ai+bi)(aj,bj)ai(aj) 即视为 ( − a j , − b j ) → ( a i , b i ) (-a_j,-b_j)\rightarrow (a_i,b_i) (aj,bj)(ai,bi) 的路径方案数。

那么这道题就是多源汇路径数了。

在一开始读入数据的时候,起点方案数增加一, f [ − a i ] [ − b i ] + + f[-a_i][-b_i]++ f[ai][bi]++

然后暴力转移 d p dp dp f i , j ← + f i − 1 , j + f i , j − 1 f_{i,j}\leftarrow^+ f_{i-1,j}+f_{i,j-1} fi,j+fi1,j+fi,j1,求出到 f [ a i ] [ b i ] f[a_i][b_i] f[ai][bi] 的方案数。

网格图的坐标 ∈ [ − 2000 , 2000 ] \in[-2000,2000] [2000,2000],整体平移 2 e 3 2e3 2e3 ,这都是细节实现。

code

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mod 1000000007
#define MAX 2000
int a[200005], b[200005];
int fac[8005], inv[8005];
int f[4005][4005];
int n;

int qkpow( int x, int y ) {
    int ans = 1;
    while( y ) {
        if( y & 1 ) ans = ans * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return ans;
}

int C( int n, int m ) { 
    if( n < m ) return 0;
    else return fac[n] * inv[m] % mod * inv[n - m] % mod;
}

signed main() {
    scanf( "%lld", &n );
    for( int i = 1;i <= n;i ++ ) {
        scanf( "%lld %lld", &a[i], &b[i] );
        f[MAX - a[i]][MAX - b[i]] ++;
    }
    fac[0] = inv[0] = 1; 
    for( int i = 1;i <= (MAX << 2);i ++ ) fac[i] = fac[i - 1] * i % mod;
    inv[MAX << 2] = qkpow( fac[MAX << 2], mod - 2 );
    for( int i = (MAX << 2) - 1;i;i -- ) inv[i] = inv[i + 1] * ( i + 1 ) % mod;
    for( int i = 0;i <= (MAX << 1);i ++ )
        for( int j = 0;j <= (MAX << 1);j ++ ) {
            if( i ) ( f[i][j] += f[i - 1][j] ) %= mod;
            if( j ) ( f[i][j] += f[i][j - 1] ) %= mod;
        }
    int ans = 0, cnt = 0;
    for( int i = 1;i <= n;i ++ ) {
        ( cnt += C( a[i] + b[i] << 1, a[i] << 1 ) ) %= mod;
        ( ans += f[MAX + a[i]][MAX + b[i]] ) %= mod;
    }
    printf( "%lld\n", ( ans - cnt + mod ) % mod * qkpow( 2, mod - 2 ) % mod );
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值