#dp,模型转换,排列组合#AT1983 [AGC001E] BBQ Hard
题目
有两个长度为\(n\)的序列\(a,b\),需要求
\[\sum_{i=1}^n\sum_{j=i+1}^nC(a_i+b_i+a_j+b_j,a_i+a_j)
\]
其中\(n\leq 200000,a,b\leq 2000\)
分析
突破口在\(a,b\)的小范围,观察后面这一坨东西
也就是一个长宽分别为\(a_i+a_j,b_i+b_j\)的矩阵
从左下角走到右上角只能通过向上或者向右的方法,这东西可以用\(dp\)解决
那我如果可以把所有\((a_i,b_i)\)扔到\(dp\)里解决自然就搞定了,考虑将坐标平移
那么也就是求\((-a_i,-b_i)\)到\((a_j,b_j)\)的方案数,
那这样互不干扰给每一个起点加1,直接大力\(dp\)
注意先要减去\((-a_i,-b_i)\)到\((a_i,b_i)\)的方案然后再除以2(按照题意)
代码
#include<cstdio>
#include<cctype>
#define rr register
using namespace std;
const int M=4003,N=200011,mod=1000000007;
int dp[M][M],n,fac[M<<1],inv[M<<1],a[N],b[N],ans;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline signed c(int n,int m){return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
signed main(){
n=iut(),fac[0]=fac[1]=inv[0]=inv[1]=1;
rr int t=M>>1,T=t<<2;
for (rr int i=2;i<=T;++i) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
for (rr int i=2;i<=T;++i) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*inv[i-1]*inv[i]%mod;
for (rr int i=1;i<=n;++i){
a[i]=iut(),b[i]=iut();
++dp[t-a[i]][t-b[i]];
}
for (rr int i=1;i<M;++i)
for (rr int j=1;j<M;++j){
rr int w=mo(dp[i-1][j],dp[i][j-1]);
dp[i][j]=mo(dp[i][j],w);
}
for (rr int i=1;i<=n;++i){
ans=mo(ans,dp[t+a[i]][t+b[i]]);
ans=mo(ans,mod-c(a[i]*2+b[i]*2,a[i]*2));
}
return !printf("%d\n",1ll*ans*inv[2]%mod);
}