其他分享
首页 > 其他分享> > AT1983 [AGC001E] BBQ Hard(组合计数)

AT1983 [AGC001E] BBQ Hard(组合计数)

作者:互联网

题意

有 \(n\) 个数对 \((a_i,b_i)\),求:

\(\sum_{i=1}^{n} \sum_{j=i+1}^{n} C_{a_i+b_i+a_j+b_j}^{a_i+a_j}\)

数据范围

\(2 \leq N \leq 200000\)。

\(1\leq a_i,b_i \leq 2000\)。

思路

预处理出阶层,直接枚举的时间复杂度为 \(O(n^2)\)。显然需要更优的做法。

可以考虑题目要求的式子的几何意义,\(C_{a_i+b_i+a_j+b_j}^{a_i+a_j}\) 可以看成是从直角坐标系的原点 \((0,0)\),只向上或向右走,走到 \((a_i+a_j,b_i+b_j)\) 的方案数(因为只向上或向右走,需要走 \(a_i+a_j+b_i+b_j\) 步,而其中 \(a_i+a_j\) 步是要向右走的,枚举哪几步向右走就是这个公式)。

但是这样还是无法做到更优的复杂度。可以考虑进一步转化为从 \((-a_i,-b_i)\) 走到 \((a_j,b_j)\) 的方案数,含义不变。对于每一个终点 \((a_i,b_i)\),都可以从所有的 \((-a_j,-b_j)\) 走过来,可以直接 \(O(\max (a_i+a_j)^2)\) dp 算出所有的答案(具体实现可以看代码)。这样计算会多算 \(i \geq j\) 的方案,于是可以先减去从 \((-a_i,-b_i)\) 走到 \((a_i,b_i)\) 的方案数,最终再把答案除以 \(2\) (乘以 \(2\) 的逆元)。

最终的时间复杂度就是 \(O(n+4000^2)\)。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
const int N=4040,NLC=2020,M=2e5+10;
int f[N][N],ans,n,inv[N<<1],fac[N<<1],infac[N<<1],a[M],b[M];
void init()
{
	fac[0]=fac[1]=inv[0]=inv[1]=infac[0]=infac[1]=1;
	for(int i=2;i<N<<1;i++)
	{
		fac[i]=1ll*fac[i-1]*i%mod;
		inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
		infac[i]=1ll*infac[i-1]*inv[i]%mod;
	}
}
int C(int n,int m){return 1ll*fac[n]*infac[m]%mod*infac[n-m]%mod;}
int main()
{
	init();
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]),f[NLC-a[i]][NLC-b[i]]++;//直接储存负数会 RE,需要整体加上一个偏移量
	for(int i=1;i<N;i++)
	    for(int j=1;j<N;j++)
	        f[i][j]=(f[i][j]+f[i][j-1]+f[i-1][j])%mod;
	for(int i=1;i<=n;i++)
	{
		ans=(ans+f[NLC+a[i]][NLC+b[i]])%mod;
		ans=((ans-C(2*a[i]+2*b[i],2*a[i]))%mod+mod)%mod;
	}
	printf("%d\n",1ll*ans*inv[2]%mod);
	return 0;
}

标签:int,复杂度,Hard,AGC001E,AT1983,leq,向右走,include,可以
来源: https://www.cnblogs.com/NLCAKIOI/p/15541183.html