2018.10.06 atcoder BBQ Hard(组合数学+dp)

版权声明:随意转载哦......但还是请注明出处吧: https://blog.csdn.net/dreaming__ldx/article/details/82955195

传送门
题上说了那么多。
其实就是求: i = 1 n j = i + 1 n ( C ( a i + a j + b i + b j   ,   a i + a j ) ) \sum_{i=1}^n \sum_{j=i+1}^n(C(a_i+a_j+b_i+b_j \ , \ a_i+a_j))
我们知道 C ( i , j ) C(i,j) 可以对应二维平面内规定每次只能向上或者向右走,最终从 ( 0 , 0 ) (0,0) ( a , b ) (a,b) 的方案数量。
那么 C ( a i + a j + b i + b j , a i + a j ) C(a_i+a_j+b_i+b_j,a_i+a_j) 就可以对应从 ( a i , b i ) (-a_i,-b_i) ( a j , b j ) (a_j,b_j) 的方案数。
这样递推一个 f f 数组表示方案数。
那么 j = i + 1 n ( C ( a i + a j + b i + b j   ,   a i + a j ) ) \sum_{j=i+1}^n(C(a_i+a_j+b_i+b_j \ , \ a_i+a_j)) 就等于 f ( a i , b i ) f(a_i,b_i) 减去 ( a i , b i ) (-a_i,-b_i) ( a i , b i ) (a_i,b_i) 的方案数。
后者可以直接组合数一波。
前者用 d p dp 预处理就行了。
代码:

#include<bits/stdc++.h>
#define N 200005
#define M 4005
#define mod 1000000007
#define ll long long
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,f[M][M],a[N],b[N];
ll ans=0,ifac[8005],fac[8005];
inline ll calc(int a,int b){return fac[a]*ifac[b]%mod*ifac[a-b]%mod;}
int main(){
	n=read();
	for(int i=1;i<=n;++i)a[i]=read(),b[i]=read(),++f[2001-a[i]][2001-b[i]];
	for(int i=1;i<=4001;++i)for(int j=1;j<=4001;++j)f[i][j]=(f[i][j]+f[i-1][j]+f[i][j-1])%mod;
	fac[0]=1,ifac[1]=1;
	for(int i=1;i<=8002;++i)fac[i]=fac[i-1]*1ll*i%mod;
	for(int i=2;i<=8002;++i)ifac[i]=(mod-mod/i)*ifac[mod%i]%mod;
	for(int i=2;i<=8002;++i)(ifac[i]*=ifac[i-1])%=mod;
	for(int i=1;i<=n;++i)(ans+=f[2001+a[i]][2001+b[i]])%=mod;
	for(int i=1;i<=n;++i)ans=((ans-calc((a[i]+b[i])<<1,b[i]<<1))%mod+mod)%mod;
	ans=ifac[2]*ans%mod;
	cout<<ans;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/dreaming__ldx/article/details/82955195