<span>题解「AT1983 [AGC001E] BBQ Hard」</span>

转载注明来源:https://www.cnblogs.com/syc233/p/13627377.html


这题的模型转化挺巧妙的,不过也都是套路。


套路:从棋盘的 \((0,0)\) 走到 \((n,m)\) ,每步只能向上或向右走的方案数为 \({n+m \choose n}\)

考虑一个DP:令 \(f_{i,j}\) 表示从 \((0,0)\) 走到 \((i,j)\) 的方案数,有转移方程:

\[f_{i,j}=f_{i-1,j}+f_{i,j-1} \]

尝试用组合数替换一下,得:

\[{i+j \choose i}={i+j-1 \choose i-1}+{i+j+1 \choose i} \]

这恰好是杨辉三角,说明 \(f_{i,j}={i+j \choose i}\)


回到这道题。

由上面的套路,不难想出一个解法:枚举 \(i,j\) ,求出 \((0,0)\) 走到 \((a_i+a_j,b_i+b_j)\) 的方案数。

然而这和暴力没什么两样,都是 \(O(n^2)\) 级别的,因为终点与 \(i,j\) 都有关。

尝试平移下棋盘,则变成了求 \((-a_i,-b_i)\) 走到 \((a_j,b_j)\) 的方案数。

这样就好统计了。将每一个 \(f_{-a_i,-b_i}\) 赋初值 \(1\) ,DP统计答案即可。

因为题目中 \(i \not =j\) ,还要减去 \(i=j\) 的情况,即从 \((-a_i,-b_i)\) 走到 \((a_i,b_i)\) 的方案数,即 \({2a_i+2b_i \choose 2a_i}\)

因为每一对 \((i,j)\) 只算一次,所以最后答案还要除以 \(2\)

因为是模意义下除以 \(2\) ,所以应该乘上 \(2\)\(10^9+7\) 意义下的逆元。


\(\text{Code}:\)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 200005
#define maxm 2001
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const lxl mod=1e9+7;

template <typename T>
inline void read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	x*=f;
}

int N,A[maxn],B[maxn];
lxl f[(maxm<<1)+5][(maxm<<1)+5],ans;
lxl inv[(maxm<<2)+5],fac[(maxm<<2)+5],inv2[(maxm<<2)+5];

inline void init()
{
	inv[0]=inv[1]=fac[0]=fac[1]=inv2[0]=inv2[1]=1;
	for(int i=2;i<=(maxm<<2);++i)
	{
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		fac[i]=fac[i-1]*i%mod;
		inv2[i]=inv2[i-1]*inv[i]%mod;
	}
}

inline lxl C(int n,int m)
{
	return fac[n]*inv2[m]%mod*inv2[n-m]%mod;
}

int main()
{
	// freopen("AT1983.in","r",stdin);
	read(N);
	init();
	for(int i=1;i<=N;++i)
	{
		read(A[i]),read(B[i]);
		++f[maxm-A[i]][maxm-B[i]];
	}
	for(int i=1;i<=(maxm<<1);++i)
		for(int j=1;j<=(maxm<<1);++j)
			(f[i][j]+=(f[i-1][j]+f[i][j-1])%mod)%=mod;
	for(int i=1;i<=N;++i)
	{
		(ans+=f[maxm+A[i]][maxm+B[i]])%=mod;
		(ans+=mod-C(2*A[i]+2*B[i],2*A[i]))%=mod;
	}
	printf("%lld\n",ans*inv[2]%mod);
	return 0;
}

全部评论

相关推荐

不愿透露姓名的神秘牛友
11-29 12:19
点赞 评论 收藏
分享
手撕没做出来是不是一定挂
Chrispp3:不会,写出来也不一定过
点赞 评论 收藏
分享
ArisRobert:统一解释一下,第4点的意思是,公司按需通知员工,没被通知到的员工是没法去上班的,所以只要没被通知到,就自动离职。就是一种比较抽象的裁员。
点赞 评论 收藏
分享
10-14 23:01
已编辑
中国地质大学(武汉) Java
CUG芝士圈:虽然是网上的项目,但最好还是包装一下,然后现在大部分公司都在忙校招,十月底、十一月初会好找一些。最后,boss才沟通100家,别焦虑,我去年暑假找第一段实习的时候沟通了500➕才有面试,校友加油
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务