[ARC124F]Chance Meeting

Chance Meeting

题解

由于最开始两者分别在第 1 1 1行与第 h h h行,结束时它们分别在第 h h h行与第 1 1 1行,所以我们可以知道,它们在图中必然有一段时间是在同一行的,此时 D D D u u u的操作总共进行了 n n n次。
我们的目的是让它们在同一行的这个时间段中,有且仅有一个时刻在同一列。
我们不妨设它们在第 i i i列相遇的合法方案数为 f i f_{i} fi,先不考虑这一段时间内两者 D D D操作与 u u u操作的区别,它们都是让两者在行上接近。
如果不考虑唯一一次相遇的话,我们是很容易算出方案的,设 g i g_{i} gi表示总的方案数,之前总共走了 2 i + h − 3 2i+h-3 2i+h3步,其中 D D D u u u h − 1 h-1 h1步, R R R r r r各占 i − 1 i-1 i1步,所以有 g i = ( 2 i + h − 3 h − 1 ) ( 2 i − 2 i − 1 ) g_{i}=\binom{2i+h-3}{h-1}\binom{2i-2}{i-1} gi=(h12i+h3)(i12i2)

通过 g g g,我们可以用容斥的方法求出 f f f,我们可以枚举不合法方案中距最后一个交点最近的交点,在这个交点后,两者不能再次相遇,这明显是一个卡塔兰数。枚举一个走第一步,那么另一个必然走最后一步,而中间的怎么走就是个卡塔兰。
容易得到,
f i = g i − 2 ∑ j = 1 i − 1 g j c i − j f_{i}=g_{i}-2\sum_{j=1}^{i-1}g_{j}c_{i-j} fi=gi2j=1i1gjcij
后面一部分很明显可以用NTT求出,下标的和是定值。

求出 f f f后我们有应该怎么求出答案了。
可以发现,前半部分的情况与后半部分的情况是对称的,我们既可以从前面到后面,也可以从后面到前面。
前面走了 i i i步,那后面就要走 n − i − 1 n-i-1 ni1步,两者都是最后一步时相遇,并在一起就是个合法方案。
同时注意此时还需加上我们之前未考虑的 U U U d d d两种操作的区别。
所以可以得到,
A n s = ( 2 h − 2 h ) ∑ i = 1 w f i f w − i + 1 Ans=\binom{2h-2}{h}\sum_{i=1}^{w}f_{i}f_{w-i+1} Ans=(h2h2)i=1wfifwi+1
简单 d p dp dp一下即可。

时间复杂度 O ( h + w l o g   w ) O\left(h+wlog\,w\right) O(h+wlogw)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 600005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int mo=998244353;
const int jzm=2333;
const int lim=10000000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-9;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int h,w,g[MAXN],t[MAXN],dp[MAXN],tp[MAXN],fac[MAXN],inv[MAXN],f[MAXN],rev[MAXN],ans;
void init(){
	fac[0]=fac[1]=inv[0]=inv[1]=f[1]=1;
	for(int i=2;i<=6e5;i++){
		fac[i]=1ll*i*fac[i-1]%mo;
		f[i]=1ll*(mo-mo/i)*f[mo%i]%mo;
		inv[i]=1ll*f[i]*inv[i-1]%mo;
	}
}
int C(int x,int y){
	if(x<0||y<0||x<y)return 0;
	return 1ll*fac[x]*inv[y]%mo*inv[x-y]%mo;
}
void NTT(int lim,int *A,int typ){
	for(int i=0;i<lim;i++)if(i<rev[i])swap(A[i],A[rev[i]]);
	for(int i=1;i<lim;i<<=1){
		int Wn=qkpow(typ^-1?orG:invG,(mo-1)/(i<<1),mo);
		for(int j=0;j<lim;j+=(i<<1))
			for(int k=0,W=1;k<i;k++,W=1ll*W*Wn%mo){
				const int x=A[j+k],y=1ll*W*A[i+j+k]%mo;
				A[j+k]=add(x,y,mo);A[i+j+k]=add(x,mo-y,mo);
			} 
	}
	if(typ^-1)return ;const int iv=qkpow(lim,mo-2,mo);
	for(int i=0;i<lim;i++)A[i]=1ll*A[i]*iv%mo;
}
signed main(){
	read(h);read(w);init();int tmp=C(h+h-2,h-1);
	for(int i=1;i<=w;i++)
		dp[i]=g[i]=1ll*C(i+i+h-3,h-1)*C(i+i-2,i-1)%mo,
		t[i]=add(C(2*i-2,i-1),mo-C(2*i-2,i-2),mo);
	int L=0,lim=1;while(lim<=2*w)lim<<=1,L++;
	for(int i=0;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<L-1);
	NTT(lim,g,1);NTT(lim,t,1);for(int i=0;i<lim;i++)tp[i]=1ll*g[i]*t[i]%mo;NTT(lim,tp,-1);
	for(int i=1;i<=w;i++)dp[i]=add(dp[i],mo-add(tp[i],tp[i],mo),mo);
	for(int i=1;i<=w;i++)ans=add(ans,1ll*dp[i]*dp[w-i+1]%mo,mo);
	printf("%d\n",1ll*tmp*ans%mo);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值