AT1983 BBQ Hard

呦,来一次久违的BBQ吧!
AT题…日本的题库质量一向很高
这题是有关组合数的DP…

  • 前置芝士

1.快速计算组合数,具体还是自行百度.
2.膜域下的除法.

  • 具体做法

题目中的问题:
∑ i = 1 n ∑ j = i + 1 n ( a i + b i + a j + b j a i + a j ) \sum_{i=1}^{n}\sum_{j=i + 1}^{n}{a_i+b_i+a_j+b_j \choose a_i+a_j} i=1nj=i+1n(ai+ajai+bi+aj+bj)
提出 ( a i + b i + a j + b j a i + a j ) {a_i+b_i+a_j+b_j \choose a_i+a_j} (ai+ajai+bi+aj+bj)这部分
感性理解后,可以发现这是原点到 ( a i + b i , a j + b j ) (a_i+b_i,a_j+b_j) (ai+bi,aj+bj)的路径的个数.
但是这样就会想到O(N2)枚举,但是这样显然是T的,所以我们还要继续优化这个式子
将原点平移可以理解为 ( a i , a j ) (a_i,a_j) (ai,aj) ( b i , b j ) (b_i,b_j) (bi,bj)的路径个数
是不是觉得很好处理了.
但是 ∑ i = 1 n ∑ j = i + 1 n \sum_{i=1}^{n}\sum_{j=i + 1}^{n} i=1nj=i+1n中并没有自己到自己的条数,所以需要减去这些多出来的条数
这里如果使用杨辉三角预处理要O(40002*2)感觉十分的不保险,所以就要使用到快速计算组合数.

  • 代码

#include<bits/stdc++.h>
#define rap(i,first,last) for(int i=first;i<=last;++i)
using namespace std;
const int maxN=2005;
const int mod=1e9+7;
const int inv2=500000004;//预处理2的逆元
long long fac[maxN*4+100],inv[maxN*4+100];
long long dp[maxN*2+100][maxN*2+100],N;
int A[1000005],B[1000005];
long long C(int n,int m)//计算组合数
{
	return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
	//预处理组合数
	fac[0]=inv[0]=inv[1]=1;
	rap(i,1,maxN*4)fac[i]=1ll*fac[i-1]*i%mod;
	rap(i,2,maxN*4)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	rap(i,1,maxN*4)inv[i]=1ll*inv[i-1]*inv[i]%mod;
	scanf("%d",&N);
	rap(i,1,N)
	{
		scanf("%d%d",&A[i],&B[i]);
		dp[maxN-A[i]][maxN-B[i]]++;//在-A[i],-B[i]位置加一
	}
	rap(i,-maxN+1,maxN)
	rap(j,-maxN+1,maxN)
	{
		dp[maxN+i][maxN+j]+=dp[maxN+i-1][maxN+j]+dp[maxN+i][maxN+j-1];//类似杨慧三角的DP
		dp[maxN+i][maxN+j]%=mod;
	}
	long long answer=0;
	rap(i,1,N)
	{
		answer+=dp[maxN+A[i]][maxN+B[i]];//对答案加上这个位置的方案数
		answer%=mod;
		answer-=C(A[i]*2+B[i]*2,A[i]*2);//减去自己到自己的方案数
		answer=(answer+mod)%mod;
	}
	answer*=inv2;//可以发现多计算了一遍,需要除二,在膜域中也就是乘上2的逆元
	answer%=mod;
	printf("%lld\n",answer);//输出answer
	return ~0;
}

看似一道简单的DP,其中涉及了各种方面的知识点只有对膜域和组合数掌握的很好才能在做这类题时得心应手.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值