Examples

AGC040F Two Pieces 解题报告

AGC040F Two Pieces 解题报告:

更好的阅读体验

题意

数轴上有两个棋子,初始都在 \(0\) 位置,进行 \(n\) 次操作,每次将一个棋子移动一步或者是把靠后的棋子移到靠前的棋子的位置,两个棋子无法区分,求最后两个棋子分别到 \(A,B\) 的方案数。

\(1\leqslant n\leqslant 10^7\)

分析

orz p_b_p_b。

不妨令 \(1\) 为坐标较大的棋子,\(2\) 为另一个,令 \(A\geqslant B\)

\(2\) 的坐标和 \(1\) 的坐标作为数对,看作二维平面上的一个点。我们画出直线 \(y=x\),那么让 \(1\) 走一步就是向右(记为操作 \(1\)),让 \(2\) 走一步就是向上(记为操作 \(2\)),让 \(2\) 跳到 \(2\) 就是跳到直线上(记为操作 \(3\)),且操作 \(2\) 不能碰触直线,最后要到达 \((A,B)\)

操作 \(3\) 次数为 \(0\) 的时候很容易处理,可以类似卡特兰数使用折线法。(注意是不能碰触直线,所以要将坐标系整体左移一格)

我们发现跳到直线上很难处理,我们将操作 \(3\) 转化成将直线移到当前位置上。记最后一次操作的坐标为 \((x_0,y_0)\),那么我们的终点就应该是 \((A-1,B-(x_0-y_0))\)。(\(A-1\) 的原因上面说了)

但是还是很难考虑,我们考虑在确定了 \((x_0-y_0)\) 以及行走出来的格路之后,将操作 \(3\) 插入操作序列。观察可以得到,对于让直线变为 \(y=x-k\) 的操作 \(3\),它的插入位置一定只能是这个直线与格路的最后一个交点(容易发现满足条件的操作 \(3\)\(0,1,\cdots,k\)),于是枚举 \(x_0-y_0\),然后插板法计算即可。

复杂度 \(O(n)\)

代码

#include<stdio.h>
const int maxn=10000005,mod=998244353;
int n,a,b,ans;
int fac[maxn],nfac[maxn],inv[maxn]; 
inline int C(int a,int b){
	return b==0? 1:((a<b||b<0)? 0:1ll*fac[a]*nfac[b]%mod*nfac[a-b]%mod);
}
int main(){
	fac[0]=nfac[0]=1;
	for(int i=1;i<maxn;i++)
		fac[i]=1ll*fac[i-1]*i%mod,inv[i]=i==1? 1:(mod-1ll*(mod/i)*inv[mod%i]%mod),nfac[i]=1ll*nfac[i-1]*inv[i]%mod;
	scanf("%d%d%d",&n,&b,&a);
	if(a==0&&b==0){
		puts("1");
		return 0;
	}
	for(int i=0;i<=b&&i<a&&i<n-a;i++)//(a-1,i)
		ans=(ans+1ll*(C(i+a-1,i)-C(i+a-1,i-1)+mod)*C((n-a-i-1)+(b-i+1)-1,(b-i+1)-1))%mod;
	if(a+b==n)
		ans=(ans+0ll+C(a+b-1,b)-C(a+b-1,b-1)+mod)%mod;
	printf("%d\n",ans);
	return 0;
}
posted @ 2022-02-19 18:53  xiaoziyao  阅读(212)  评论(0)    收藏  举报