AGC001E BBQ Hard 组合计数

这篇博客介绍了如何解决一道名为AGC001E BBQ Hard的组合计数问题。通过观察坐标范围和式子结构,作者提出利用组合的意义来简化问题,将问题转化为计算特定路径的方案数。采用动态规划的方法,可以在O(坐标^2)的时间复杂度内求解,同时注意需要排除特定情况下的重复计算。最后,博客提供了实现算法的代码片段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

题目大概要求的就是 i=1nj=i+1nC(ai+aj+bi+bj,ai+aj) ∑ i = 1 n ∑ j = i + 1 n C ( a i + a j + b i + b j , a i + a j )

看这个式子发现怎么都不是很好求 观察到坐标范围不是很大 考虑组合意义

如果只能向右和向上走 并且 x<p,y<q x < p , y < q

则从点 (x,y) ( x , y ) 到点 (p,q) ( p , q ) 的方案数为 C(px+qy,px) C ( p − x + q − y , p − x )

这个理解起来不是很难 就是总步数选取多少步向右走

回到题目 先把式子化一下 就变成了

(i=1njinC(ai+aj+bi+bj,ai+aj))/2 ( ∑ i = 1 n ∑ j ≠ i n C ( a i + a j + b i + b j , a i + a j ) ) / 2

那么题目要求的式子 就可以转化成点 (aj,bj) ( − a j , − b j ) 到点 (ai,bi) ( a i , b i ) 的路径数

这个东西一个 O(2) O ( 坐 标 2 ) dp d p 就能解决 然后就做完了

当然对于每个点要记得减去 (ai,bi) ( − a i , b i ) (ai,bi) ( a i , b i ) 的方案数

Codes
#include<bits/stdc++.h>

using namespace std;

const int M = 2e5 + 10, N = 8000 + 10, mod = 1e9 + 7;
int a[M], b[M], fac[N], inv[N];
int ans, n, f[N][N];

int qpow(int a, int x) {
    int ret = 1;
    while(x) {
        if(x & 1) 
            ret = 1ll * ret * a % mod;
        x >>= 1, a = 1ll * a * a % mod;
    }
    return ret;
}

void Init(int maxn) {
    scanf("%d", &n);
    fac[0] = inv[0] = 1;
    for(int i = 1; i <= maxn; ++ i)
        fac[i] = 1ll * fac[i - 1] * i % mod;
    inv[maxn] = qpow(fac[maxn], mod - 2);
    for(int i = maxn; i >= 1; -- i)
        inv[i - 1] = 1ll * inv[i] * i % mod;
}

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

int main() {
#ifndef ONLINE_JUDGE
    freopen("AT1983.in", "r", stdin);
    freopen("AT1983.out", "w", stdout);
#endif
    Init(N - 5);
    for(int i = 1; i <= n; ++ i) {
        scanf("%d%d", &a[i], &b[i]);
        ++ f[2002 - a[i]][2002 - b[i]];
    }
    for(int i = 1; i <= 4002; ++ i)
        for(int j = 1; j <= 4002; ++ j) 
            (f[i][j] += (f[i - 1][j] + f[i][j - 1]) % mod) %= mod;
    for(int i = 1; i <= n; ++ i)
        (ans += f[a[i] + 2002][b[i] + 2002] - C((a[i] + b[i]) * 2, a[i] * 2)) %= mod;
    cout << 1ll * (mod + 1) / 2 * (ans + mod) % mod << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值