原题链接
题目大意
有一个
n
×
m
(
1
≤
n
,
m
≤
5
×
1
0
5
)
n\times m(1\le n,m\le 5\times 10^5)
n×m(1≤n,m≤5×105)大小的迷宫,两角坐标为
(
1
,
1
)
(1,1)
(1,1)和
(
n
,
m
)
(n,m)
(n,m)。
若你当前在
(
x
,
y
)
(x,y)
(x,y)位置,美妙你可以向上下左右四个方向移动一格,不能停留在原地,不能移动到迷宫外。
初始在 ( a , b ) (a,b) (a,b)处,继续移动 t ( 1 ≤ n ≤ 5 × 1 0 5 ) t(1\le n\le 5\times 10^5) t(1≤n≤5×105)秒,求有多少种合法的移动路径。
题解
求路径方案数,我们常用DP推出动态转移式来计算,但本题如果我们直接计算所有方案的话,算法复杂度会很高,并且不容易实现。不妨换种思路,我们把所有路径拆成不同的情况,即上下移动、左右移动操作并进行分别考虑。
假设上下移动的方案为
X
i
(
1
≤
i
≤
T
)
X_i(1\le i\le T)
Xi(1≤i≤T),左右移动为
Y
j
(
1
≤
j
≤
T
)
Y_j(1\le j\le T)
Yj(1≤j≤T)(
i
、
j
i、j
i、j分别为操作步数)。
则我们可以得到答案
a
n
s
=
∑
i
=
0
T
X
i
×
Y
T
−
i
×
C
T
i
ans=\sum^{T}_{i=0}X_i\times Y_{T-i}\times C^{i}_{T}
ans=∑i=0TXi×YT−i×CTi(
C
C
C为上下方向和左右方向的所有组合,
i
=
0
i=0
i=0时即为在上下或左右方向上不动的情况,此时
X
1
=
Y
1
=
1
X_1=Y_1=1
X1=Y1=1)
然后就是对 X X X以及 Y Y Y的计算。
方案一:首先,我们假设上下(或左右)方向移动方案为
d
p
t
,
x
dp_{t,x}
dpt,x(
t
t
t为移动步数,
x
x
x表示移动至第几行或第几列即状态)。
然后我们可以发现,在同一方向(即上下或左右)上的某一个位置有两个点可以到达此点(除去边界只有一个点可以到达,但是在实现代码时并不影响计算值),所以我们可以得到转移式为:
d
p
t
,
x
=
d
p
t
−
1
,
x
−
1
+
d
p
t
−
1
,
x
+
1
dp_{t,x}=dp_{t-1,x-1}+dp_{t-1,x+1}
dpt,x=dpt−1,x−1+dpt−1,x+1
但是这种方案的时间复杂的为
O
(
T
n
)
O(Tn)
O(Tn),在数据非常大的时候会TLE,我们再想想别的解决方案。
假设
K
(
i
)
K(i)
K(i)为第
i
i
i步的方案数。
我们先将
d
p
t
,
x
dp_{t,x}
dpt,x分解成
d
p
t
−
1
,
x
−
1
+
d
p
t
−
1
,
x
+
1
dp_{t-1,x-1}+dp_{t-1,x+1}
dpt−1,x−1+dpt−1,x+1,不难发现,每个
d
p
t
,
x
dp_{t,x}
dpt,x对方案的贡献系数为2(除去边界的两个点的贡献系数为1)。然后我们可以得到
K
(
i
)
=
2
×
K
(
i
−
1
)
−
d
p
i
−
1
,
1
−
d
p
i
−
1
,
n
K(i)=2\times K(i-1)-dp_{i-1,1}-dp_{i-1,n}
K(i)=2×K(i−1)−dpi−1,1−dpi−1,n,所以我们只需要考虑两个特殊值即可。
以计算
d
p
i
−
1
,
1
dp_{i-1,1}
dpi−1,1为例(
d
p
i
−
1
,
n
dp_{i-1,n}
dpi−1,n同理)
如果可以无限制的跑,我们假设从
x
x
x走
i
i
i步到
y
y
y的方案为
P
(
y
)
P(y)
P(y),是一个组合数值。
但如果要算出
d
p
t
−
1
,
1
dp_{t-1,1}
dpt−1,1的值我们就需要在
P
(
1
)
P(1)
P(1)的基础上减去不合法情况。
假设点
w
w
w以左边界点
0
0
0为对称轴的位置为
L
(
w
)
=
−
2
w
L(w)=-2w
L(w)=−2w,以右边界点
n
+
1
n+1
n+1为对称轴的位置为
R
(
w
)
=
2
×
(
n
+
1
)
−
w
R(w)=2\times (n+1)-w
R(w)=2×(n+1)−w。
通过容斥考虑重复计算的方案,可以得到:
d
p
t
−
1
,
1
=
P
(
1
)
−
P
(
L
(
1
)
)
−
P
(
R
(
1
)
)
+
P
(
L
(
R
(
1
)
)
)
+
P
(
R
(
L
(
1
)
)
)
−
.
.
.
dp_{t-1,1}=P(1)-P(L(1))-P(R(1))+P(L(R(1)))+P(R(L(1)))-...
dpt−1,1=P(1)−P(L(1))−P(R(1))+P(L(R(1)))+P(R(L(1)))−...
因为每次对称移动的距离至少为
n
n
n(或
m
m
m),所以此时每种方案的时间复杂度为
O
(
i
n
)
O(\frac{i}{n})
O(ni),总时间复杂度为
O
(
T
2
n
)
O(\frac{T^2}{n})
O(nT2)。
通过对比两种解决方案的复杂度,我们分情况进行采用。可以发现,当 n n n(或 m m m) = T =\sqrt{T} =T时,两种方案的复杂度均为 O ( T T ) O(T\sqrt{T}) O(TT),所以我们以 n n n(或 m m m) = T =\sqrt{T} =T为分界线,当 n n n(或 m m m) < T <\sqrt{T} <T时, O ( T n ) O(Tn) O(Tn)的复杂度更低,反之, O ( T 2 n ) O(\frac{T^2}{n}) O(nT2)的复杂度更低。
参考代码
#include<bits/stdc++.h>
#define mod 998244353
using namespace std;
const int N=5e5+5;
const int M=7e2+10;
int frac[N],invf[N],X[N],Y[N];
int C(int n,int m){return 1ll*frac[n]*invf[m]%mod*invf[n-m]%mod;}
int powmod(int a,int b){int ret=1;while(b){if(b&1)ret=1ll*ret*a%mod;a=1ll*a*a%mod;b>>=1;}return ret;}
void init()
{
frac[0]=1;
for(int i=1;i<N;i++)frac[i]=1ll*frac[i-1]*i%mod;
invf[N-1]=powmod(frac[N-1],mod-2);
for(int i=N-1;i;i--)
invf[i-1]=1ll*invf[i]*i%mod;
}
int cal(int s,int n,int a,int pos)
{
int pos1=pos,pos2=pos,ans=0,d=abs(pos-a);
if(d<=s&&(s-d)%2==0)ans=C(s,(s+d)/2);
for(int j=1;;j++)
{
if(j&1){pos1=-pos1;pos2=2*(n+1)-pos2;}
else{pos1=2*(n+1)-pos1;pos2=-pos2;}
int d1=abs(pos1-a),d2=abs(pos2-a),det=0;
if(d1>s&&d2>s)break;
if(d1<=s&&(s-d1)%2==0)det=(det+C(s,(s+d1)/2))%mod;
if(d2<=s&&(s-d2)%2==0)det=(det+C(s,(s+d2)/2))%mod;//如果s-d2不是偶数,即不可能走到那个节点,为非法情况
if(j&1)ans=(ans+mod-det)%mod;
else ans=(ans+det)%mod;
}
return ans;
}
int dp[2][N];
int n,m,x,y,t;
void w()
{
if(1ll*n*n<=t)
{
dp[0][x]=1;X[0]=1;
for(int i=1;i<=t;i++)
{
dp[1][1]=dp[0][2];X[i]=(X[i]+dp[1][1])%mod;
dp[1][n]=dp[0][n-1];X[i]=(X[i]+dp[1][n])%mod;
for(int j=2;j<n;j++)
{
dp[1][j]=(dp[0][j-1]+dp[0][j+1])%mod;
X[i]=(X[i]+dp[1][j])%mod;
dp[0][j-1]=dp[1][j-1];
}
dp[0][n-1]=dp[1][n-1];
dp[0][n]=dp[1][n];
}
for(int j=1;j<=n;j++)
dp[0][j]=dp[1][j]=0;
}
if(1ll*m*m<=t)
{
dp[0][y]=1;Y[0]=1;
for(int i=1;i<=t;i++)
{
dp[1][1]=dp[0][2];Y[i]=(Y[i]+dp[1][1])%mod;
dp[1][m]=dp[0][m-1];Y[i]=(Y[i]+dp[1][m])%mod;
for(int j=2;j<m;j++)
{
dp[1][j]=(dp[0][j-1]+dp[0][j+1])%mod;
Y[i]=(Y[i]+dp[1][j])%mod;
dp[0][j-1]=dp[1][j-1];
}
dp[0][m-1]=dp[1][m-1];
dp[0][m]=dp[1][m];
}
for(int j=1;j<=m;j++)
dp[0][j]=dp[1][j]=0;
}
//分情况讨论,分界值即为根号T
if(1ll*n*n>t)
{
X[0]=1;
for(int i=1;i<=t;i++)
X[i]=(2ll*X[i-1]+mod-cal(i-1,n,x,1)+mod-cal(i-1,n,x,n))%mod;
}
if(1ll*m*m>t)
{
Y[0]=1;
for(int i=1;i<=t;i++)
Y[i]=(2ll*Y[i-1]+mod-cal(i-1,m,y,1)+mod-cal(i-1,m,y,m))%mod;
}
}
int main()
{
init();
scanf("%d%d%d%d%d",&t,&n,&m,&x,&y);
w();
int ans=0;
for(int i=0;i<=t;i++)
ans=(ans+1ll*C(t,i)*X[i]%mod*Y[t-i]%mod)%mod;
printf("%d",ans);
}