题目:传送门
题意: 给你一个数组,数组中的-1可以变为1~k的任意数,我们要求不存奇回文串,问我们有多少种选法,答案对998244353取模.
思路: 要求不存在奇回文串,说明相邻的偶数标号的数不相同(奇数同理).我们不妨用dp [i] [j]表示到i时选择第j个数的方案数.则数组{1,-1,-1,3,-1,-1,-1,3},k为4时.
奇序列 | 1 | -1 | -1 | -1 |
---|---|---|---|---|
1 | 1 | 0 | 3 | 6 |
2 | 0 | 1 | 2 | 7 |
3 | 0 | 1 | 2 | 7 |
4 | 0 | 1 | 2 | 7 |
偶序列 | -1 | 3 | -1 | 3 |
---|---|---|---|---|
1 | 1 | 0 | 3 | 0 |
2 | 1 | 0 | 3 | 0 |
3 | 1 | 3 | 0 | 9 |
4 | 1 | 0 | 3 | 0 |
我们发现,对于相同的i,我们就只有两种不同的状态,所以我们的DP可以简化。使用DP[i][J]表示到I时是否与左边最后出现的不为-1的数相同的方案总数。具体内容请看代码:
#include <iostream>
using namespace std;
const long long mods = 998244353;
long long a[200050];
long long dp[200050][3];
long long last[200050];//表示到I时最后出现的不为-1的数的大小
long long yz[200050];//因为!=last【i】时每种选择方案数的大小是相同,我们这样是为了便于计算,因为我们dp计算的是总数,而中途会进行除法运算.除法运算使用逆元取模.
int main() {
int n,k;
cin>>n>>k;
for(int i=0;i<n;i++) {
cin>>a[i];
if(i<2) last[i] = a[i];
else last[i] = (a[i]==-1? last[i-2]:a[i]);
}
//将奇序列,偶序列开头的数进行预处理。
if(last[0]==-1){
dp[0][0] = k;
yz[0]=1;
dp[0][1] =0;
}
else {
dp[0][0] = 0;
dp[0][1] = 1;
yz[0]=0;
}
if(last[1]==-1) {
dp[1][0] = k;
dp[1][1] =0;
yz[1]=1;
}
else {
dp[1][0] = 0;
dp[1][1] = 1;
yz[1]=0;
}
for(int i=2;i<n;i++) {
if(a[i]==-1) {
if(last[i-2] != -1) { // 表示前面有不为-1的数
dp[i][1]= dp[i-2][0]%mods;
yz[i]=(yz[i-2]*(k-2)+dp[i-2][1])%mods;
dp[i][0]= (dp[i-2][1]*(k-1)%mods+dp[i-2][0]*(k-2)%mods)%mods;
}
else { // 说明前面全为-1.
dp[i][1] = dp[i-2][1];
dp[i][0] = dp[i-2][0]*(k-1)%mods;
yz[i] = yz[i-2]*(k-1)%mods;
}
}
else {//如果自身不为-1时的情况
if(last[i-2] != -1) {
if(last[i-2] != a[i]) {
dp[i][0] = 0;
yz[i]=0;
dp[i][1] = (dp[i-2][1]%mods+yz[i-2]*(k-2)%mods)%mods;
}
else {
dp[i][0] = 0;
dp[i][1] = dp[i-2][0];
yz[i] = 0;
}
}
else {
dp[i][0] = 0;
yz[i]=0;
dp[i][1] = yz[i-2]*(k-1)%mods;
}
}
}
///*if(n==200000&&k== 40002) */cout<<dp[n-1][1]+dp[n-1][0]<<' '<<dp[n-2][1]+dp[n-2][0]<<endl;
cout<<(dp[n-1][1]+dp[n-1][0])%mods*((dp[n-2][1]+dp[n-2][0])%mods)%mods<<endl;
return 0;
}