AT1983 BBQ Hard

传送门

好妙的一道题,题目要求的其实是这个式子
\[ \sum_{i=1}^{n}\sum_{j=i+1}^{n}\binom{a_i+b_i+a_j+b_j}{a_i+a_j} \]
但是直接计算显然是\(O(n^2)\)的,看起来也没法优化

由于\(n\)比较大,而坐标范围比较小,我们发现,我才发现不了呢

在一个网格图中,只能向上或者向右走,从\((x,y)\)这个点走到\((a,b)\)这个点(保证\(x<a,b<y\)),方案数是\(\binom{a-x+b-y}{a-x}\)吗,这和上面的式子很像

所以我们将每个点对\((a_i,b_i)\)拆成两个点对\((-a_i,-b_i),(a_i,b_i)\)

总的方案数就是从第三象限的点走到第一象限的点的方案数除去自己走到自己的,然后最后再除以\(2\)就好了

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void read(int &x) {
    char ch; bool ok;
    for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=2e5+10,mod=1e9+7,N=4050,M=2010;
int n,a[maxn],b[maxn],s[N+10][N+10],fac[maxn],inv[maxn],ans;
int mul(int x,int y){return 1ll*x*y-1ll*x*y/mod*mod;}
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int del(int x,int y){return x-y<0?x-y+mod:x-y;}
int mi(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=mul(ans,a);
        b>>=1,a=mul(a,a);
    }
    return ans;
}
int C(int n,int m){return mul(fac[n],mul(inv[n-m],inv[m]));}
int main(){
    read(n);fac[0]=inv[0]=1;
    for(rg int i=1;i<=n;i++)read(a[i]),read(b[i]),s[M-a[i]][M-b[i]]+=1;
    for(rg int i=1;i<=N<<1;i++)fac[i]=mul(fac[i-1],i);
    inv[N<<1]=mi(fac[N<<1],mod-2);
    for(rg int i=(N<<1)-1;i;i--)inv[i]=mul(i+1,inv[i+1]);
    for(rg int i=1;i<=N;i++)
        for(rg int j=1;j<=N;j++) 
            s[i][j]=add(s[i][j],add(s[i][j-1],s[i-1][j]));
    for(rg int i=1;i<=n;i++)ans=add(ans,del(s[M+a[i]][M+b[i]],C(2*(a[i]+b[i]),2*a[i])));
    printf("%d\n",mul(ans,mi(2,mod-2)));
}
posted @ 2019-05-30 19:30 蒟蒻--lichenxi 阅读(...) 评论(...) 编辑 收藏