题意:
两个串,有一些地方已经被填上了-1,1。
现在有两个指针让你操作,每次可以选择一个指针向前移动一步,扫完序列的指针不能再动。
当两个指针走过路径的和(前缀和)小于0时就失败了。
指针的操作方式有一些限制:两个指针下一步中一个是1,另一个是-1时,必须走1的那一个指针。
现在要你把剩下的空位填上-1或1,问无论怎么走都能在不失败的情况下填数的方案。
序列长度
先考虑对于一个特定的情况合不合法。
考虑走的过程中的最小值。
实际上达到最小值的情况是走了一步-1:
- 两个指针后面都是-1
- 一个指针走到了终点
所以在走最小值前一步时,两个点都会在-1前或终点处。
所以可以只用考虑-1前面的点和终点处 两个序列的最小前缀和 之和。(下文中最小前缀和均代表-1前面的点和终点处的前缀和的最小值)
当这个最小值<1时就意味这他们无法再应对之后到来的-1,所以这里前缀和之和需要>0。
(两个点都在终点处时两个序列都不会再-1了,这时>=0即可,下文不再考虑)
有一个这样的性质:当一个指针在-1之前或结尾时,另一个指针是可以随便跑的。
所以说满足前面指针条件的任何前缀和之和<=0的情况,都可以在一个点到达目标点后直接将另一个点移至对应的最小值,导致任务失败。
所以-1前和终点处的最小前缀和的条件对答案是必要的。
“为什么这样就可行呢?如果两个后面都是-1的话为什么不可能时前缀和从1变成-1?”
考虑最小前缀和之和为1的情况:
考虑走一步。
这时候一个点在-1上,另一个点在-1前,这时前缀和之和为0。
在-1前的点能走到-1的条件是另一行的下一个点也要是-1或者另一行走到了结尾。
无论是结尾处的前缀和还是下一个-1前的前缀和显然都要被统计(这里比原最小前缀和多了个-1),这样最小前缀和就不是1了。
与假设相矛盾。
会发现这时候答案只和两行的最小前缀和有关,于是可以分开来DP。
发现从前往后DP还同时记录历史最小前缀和和结尾的前缀和,十分麻烦,于是考虑从后往前。
用DP[i][j][k(0/1)]表示目前第i位,当前串最小前缀和为j,结尾前缀和与最小前缀和的关系为k的方案数。
(只要前面有和结尾相同的前缀和,最小前缀和就不应该与结尾有关了)
这时候往前新加一个数会发现最小前缀和要么加上增量,要么变成-1(新加的数)。
两边分别DP出来后前缀和优化卷积,对符合条件的求和就行。
在实际DP时并不需要考虑后一项是不是-1的问题,只需要判断开始处的0是否需要被考虑。
code:
#include <iostream>
#include <cstdio>
#include <cstring>
typedef long long lint;
const int N=5005;
const int mod=998244353;
template <class T> inline void twk(T &x){
if(x>=mod) x-=mod;
}
inline int nxi(){
int x=0;
char c;
while((c=getchar())>'9'||c<'0');
while(x=x*10-48+c,(c=getchar())>='0'&&c<='9');
return x;
}
inline void get(int dp[][2]){
static int tdp[N<<1][2];
static char ch[N];
scanf("%s",ch+1);
int n=strlen(ch+1);
dp[N][1]=1;
for(int i=n; i; --i){
const int dis=n-i+1;
memcpy(tdp+N-dis,dp+N-dis,((dis+1)<<1)*sizeof(tdp[0]));
memset(dp+N-dis,0,((dis+1)<<1)*sizeof(tdp[0]));
for(int j=N-dis; j<=N+dis; ++j){
if(ch[i]!='V'){
if(j<=N){
twk(dp[j-1][0]+=tdp[j][0]);
twk(dp[j-1][1]+=tdp[j][1]);
}
else{
twk(dp[N][0]+=tdp[j][0]);
twk(dp[N][0]+=tdp[j][1]);
}
}
if(ch[i]!='P'){
twk(dp[j+1][0]+=tdp[j][0]);
twk(dp[j+1][1]+=tdp[j][1]);
}
}
}
}
inline int solve(int dp0[][2],int dp1[][2]){
int ans=0;
for(int i=1; i<N<<1; ++i){
ans=(ans+(lint)dp0[i][1]*dp1[(N<<1)-i][1])%mod;
}
for(int i=(N<<1)-1; i; --i){
twk(dp1[i-1][0]+=dp1[i][0]);
twk(dp1[i-1][1]+=dp1[i][1]);
}
for(int i=1; i<N<<1; ++i){
ans=(ans+(lint)(dp0[i][0]+dp0[i][1])*dp1[(N<<1)-i+1][0])%mod;
ans=(ans+(lint)(dp0[i][0]+dp0[i][1])*dp1[(N<<1)-i+1][1])%mod;
}
return ans;
}
int main(){
static int dp0[N<<1][2],dp1[N<<1][2];
get(dp0);
get(dp1);
printf("%d\n",solve(dp0,dp1));
return 0;
}