ATG1E BBQ Hard 学习笔记

ATG1E BBQ Hard 学习笔记

Luogu Link

题意简述

计算 $$\sum_{i=1}^n \sum_{j=i+1}^n \dbinom{a_i+b_i+a_j+b_j}{a_i+a_j}$$ 的值。答案对 \(10^9+7\) 取模。

\(N\le 2\times 10^5,a_i,b_i\le 2\times 10^3\)

做法解析

\(O(N^2)\) 的做法是暴力枚举 \(i,j\)\(O(1)\) 计算出 \(\dbinom{a_i+b_i+a_j+b_j}{a_i+a_j}\),将结果全部相加即可。

怎么优化?我们不妨先考虑一下其组合意义。众所周知 \(\dbinom{x+y}{x}\) 的一个组合意义是:只往右或上走,从 \((0,0)\) 走到 \((x,y)\) 的方案数(相当于往 \(x\) 个向右走操作中插入 \(y\) 个向上走操作的方案数)。所以我们要求的东西就是从 \((0,0)\) 走到所有 \((a_i+a_j,b_i+b_j)\) 的方案数。这么做起点只有一个,却要一个个统计 \(O(N^2)\) 个终点的答案,太劣了。

考虑复杂度平衡。我们把网格图从 \(((0,0),(a_i+a_j,b_i+b_j))\) 平移到 \(((-a_i,-b_i),(a_j,b_j))\)。现在就变为了 \(O(N)\) 个起点,\(O(N)\) 个终点。我们 \(O(N)\) 地将每个起点的初始方案数加一,然后递推一遍组合数,最后 \(O(N)\) 收集每个终点的方案数。优异啊!

pEMpJVe.md.png

由于题目要求只统计 \(i<j\) 的方案数,所以要减去自己做起点到自己做重点的方案数,最后乘上 \(2\) 的逆元。这道题就做完了!

代码实现

#include <bits/stdc++.h>
using namespace std;
namespace obasic{
    typedef long long lolo;
    template <typename _T>
    void readi(_T &x){
        _T k=1;x=0;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
        x*=k;return;
    }
    template <typename _T>
    void writi(_T x){
        if(x<0)putchar('-'),x=-x;
        if(x>9)writi(x/10);
        putchar(x%10+'0');
    }
};
using namespace obasic;
const int MaxN=2e5+5,MaxM=2e3+5,Mod=1e9+7,Df=2001;
int N;lolo A[MaxN],B[MaxN],facr[MaxM<<2],finv[MaxM<<2];
namespace omathe{
    lolo fastpow(lolo a,lolo b,lolo p){
        lolo res=1;
        for(;b;(a*=a)%=p,b>>=1)if(b&1)(res*=a)%=p;
        return res;
    }
    lolo getinv(lolo a,lolo p){
        return fastpow(a,p-2,p);
    }
    lolo Comb(lolo n,lolo m,lolo p){
        return facr[n]*finv[n-m]%p*finv[m]%p;
    }
};
using namespace omathe;
void prework(int n){
    facr[0]=finv[0]=1;
    for(int i=1;i<=n;i++)facr[i]=facr[i-1]*i%Mod;
    finv[n]=getinv(facr[n],Mod);
    for(int i=n-1;i;i--)finv[i]=finv[i+1]*(i+1)%Mod;
}
lolo C[MaxM<<1][MaxM<<1],ans;
int main(){
    readi(N);prework(Df*4+4);
    for(int i=1;i<=N;i++)readi(A[i]),readi(B[i]);
    for(int i=1;i<=N;i++)C[Df-A[i]][Df-B[i]]++;
    for(int i=1;i<=Df*2;i++){
        for(int j=1;j<=Df*2;j++){
            (C[i][j]+=(C[i-1][j]+C[i][j-1])%Mod)%=Mod;
        }
    }
    for(int i=1;i<=N;i++){
        (ans+=C[A[i]+Df][B[i]+Df])%=Mod;
        (ans+=Mod-Comb(A[i]*2+B[i]*2,A[i]*2,Mod))%=Mod;
    }
    (ans*=getinv(2,Mod))%=Mod;writi(ans);
    return 0;
}

反思总结

了解 \(\dbinom{x+y}{x}\) 的一个组合意义:只往右或上走,从 \((0,0)\) 走到 \((x,y)\) 的方案数。
熟练运用复杂度平衡思想,见到 \(O(1)-O(N^2)\) 的东西尝试平衡为 \(O(N)-O(N)\)

posted @ 2025-02-17 10:15  矞龙OrinLoong  阅读(5)  评论(0)    收藏  举报