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+h−3步,其中
D
D
D与
u
u
u占
h
−
1
h-1
h−1步,
R
R
R与
r
r
r各占
i
−
1
i-1
i−1步,所以有
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=(h−12i+h−3)(i−12i−2)。
通过
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=gi−2j=1∑i−1gjci−j
后面一部分很明显可以用NTT求出,下标的和是定值。
求出
f
f
f后我们有应该怎么求出答案了。
可以发现,前半部分的情况与后半部分的情况是对称的,我们既可以从前面到后面,也可以从后面到前面。
前面走了
i
i
i步,那后面就要走
n
−
i
−
1
n-i-1
n−i−1步,两者都是最后一步时相遇,并在一起就是个合法方案。
同时注意此时还需加上我们之前未考虑的
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=(h2h−2)i=1∑wfifw−i+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;
}