做题时间:2022.8.11
\(【题目描述】\)
给定 \(N(1\leq N\leq 2\times 10^5)\) 个二元组,第 \(i\) 个二元组形如 \((a_i,b_i)(1\leq a_i,b_i\leq 2000)\) ,计算:
\[\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n \binom{a_i+a_j+b_i+b_j}{a_i+a_j}\mod 10^9+7 \]\(【输入格式】\)
第一行一个整数 \(N\)
接下来 \(N\) 行每行两个整数表示 \(a_i,b_i\)
\(【输出格式】\)
一行一个数表示答案
\(【考点】\)
组合,组合意义
\(【做法】\)
柿子形如 \(\binom{a+b}{a}\) 可以想到从网格图上从 \((1,1)\) 沿网格走到 \((a,b)\) 的方案数 ,而 \(\binom{a_i+a_j+b_i+b_j}{a_i+a_j}\) 可以看成从 \((-a_i,-b_i)\) 走到 \((a_j,b_j)\) 的方案数。这样问题可以转化为坐标系上第一象限的点到第三象限的点两两方案数之和,而答案变成了:
\[\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n \binom{a_i+a_j+b_i+b_j}{a_i+a_j}=\frac{1}{2}\left(\sum\limits_{i=1}^n\sum\limits_{j=1}^n \binom{a_i+a_j+b_i+b_j}{a_i+a_j}-\sum\limits_{i=1}^n \binom{2(a_i+b_i)}{2a_i}\right) \]而 \(a_i,b_i\leq 2000\) ,因此可以直接在坐标系上dp,定义 \(f_{i,j}\) 表示从 \((i,j)\) 左下所有关键点走到 \((i,j)\) 的路径长总和,有:
\[f_{-a_i,-b_i}=cnt_{a_i,b_i} \]\[f_{i,j}=f_{i,j}+f_{i-1,j}+f_{i,j-1} \]其中 \(cnt_{x,y}\) 表示 \((x,y)\) 处的点的数量。
dp的时候坐标偏移一下即可。
dp完后减去多余贡献即可。
\(【代码】\)
#include<cstdio>
#include<iomanip>
using namespace std;
const int N=4e3+50,MAXN=2005,MOD=1e9+7;
typedef long long ll;
int a[N*50],b[N*50];
ll f[N][N],Inv[N<<1],Fac[N<<1];
int n,maxn;
void Init()
{
Inv[1]=1,Fac[0]=Fac[1]=1;
for(int i=2;i<=MAXN*4;i++) Inv[i]=(MOD-MOD/i)*Inv[MOD%i]%MOD;
for(int i=2;i<=MAXN*4;i++){
Fac[i]=Fac[i-1]*i%MOD;
Inv[i]*=Inv[i-1],Inv[i]%=MOD;
}
}
ll C(ll n,ll m)
{
return Fac[n]*Inv[m]%MOD*Inv[n-m]%MOD;
}
ll Update(ll x)
{
if(x>=MOD) x-=MOD;
return x;
}
int main()
{
Init();
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i],&b[i]);
f[-a[i]+MAXN][-b[i]+MAXN]++;
}
for(int i=1;i<=MAXN*2;i++){
for(int j=1;j<=MAXN*2;j++){
f[i][j]=Update(Update(f[i][j]+f[i-1][j])+f[i][j-1]);
}
}
ll ans=0;
for(int i=1;i<=n;i++){
ans=Update(f[a[i]+MAXN][b[i]+MAXN]+ans);
ans-=C(2*(a[i]+b[i]),2*a[i]);//减去多余的贡献
ans=(ans+MOD)%MOD;
}
printf("%lld\n",ans*Inv[2]%MOD);//最后/2
return 0;
}
标签:limits,int,sum,Hard,AGC001E,leq,dp,binom,BBQ
From: https://www.cnblogs.com/Unlimited-Chan/p/16581907.html