鏈接:
題意:
給一個長度爲n的序列a[]和k,a[i]∈[1,k] || a[i] = -1 其中有些位置爲-1,表示待填數。
定義非迴文序列:對任意(l,r),r-l+1>1且r-l+1爲奇數,對任意i屬於[0,r-l],都有a[l+i] != a[r-i]
用1~k替換-1,使原序列非迴文,求非迴文序列能構成多少個,答案對998244353取模。
思路:
根據非迴文序列的定義,可以發現同奇偶性位置的數可以分爲兩個序列:a[1],a[3]...a[n&1?n:n-1]和a[2],a[4]...a[n&1?n-1:n]
兩個序列只要相鄰兩項不等即可保證a是非迴文序列。
問題轉化爲,用1~k填充一個序列b中的空位,有多少種方案。
很明顯,這個問題需要分類討論一下:
假設序列長度爲m,
①當全部都是-1的時候:第一位可以填k個數,其餘位填k-1個。答案k*(k-1)^(m-1)
②首位爲-1,但序列不全爲-1:從首位(b[1])到第一個非-1的位(b[x]),構成的答案數量不妨從非-1位倒着推,b[x-1]有k-1種方案,b[x-2]有k-1種方案……這一段構成的答案爲(k-1)^(x-1) 其中x爲首位非-1的數的下標,x-1爲從頭開始連續-1的個數。
③最後一位爲-1,但序列不全爲-1:和②類似
④最難的是中間部分了:b[l]≠-1,b[r]≠-1,l+1~r-1之間爲-1:
繼續分爲兩類:
(1)b[l] = b[r]:一開始,假設l和r中間只有1個-1,答案很明顯產生k-1種。假設中間有2個-1,第一個-1產生的答案數還是不變,而第二個-1相當於向上一個-1和b[r]之間塞了一個數,它能產生多少種答案取決於上一個-1位置填充的數和b[r]是否相等……如果相等,產生答案爲k-1種,否則k-2種。由n個-1推n+1個-1時也如此。
設same[i][j]表示b[l]=b[r]時,第i個-1填充完成後b[l+i]?=b[r]時的方案數。其中b[l+i]==b[r]時j爲1,否則爲0.
要求的這一部分就是same[r-l][0]了。
根據上述思路,容易得出狀態轉移方程same[i][0] = same[i-1][0]*(k-2) + same[i-1][1]*(k-1)
那same[i][1]應該如何轉移呢?same[i][1]的意思就是dp到第i位與b[r]相等的方案數。其實也就是dp到第i-1位與b[r]不等的方案數。那麼轉移方程就得出來了:same[i][1] = same[i-1][0]
然後考慮初始條件,即i=0時的same[0][0]和same[0][1].b[l]=b[r],所以same[0][0]=0,same[0][1]=1
(2)b[l]!=b[r]:dp變量用diff表示,狀態轉移方程和(1)完全相同。只不過初始條件不同。diff[0][0] = 1,diff[0][1] = 0.
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 200010;
static const int INF = 0x3f3f3f3f;
static const int mod = 998244353;
static const double eps = 1e-6;
static const double pi = acos(-1);
void redirect(){
#ifdef LOCAL
freopen("test.txt","r",stdin);
#endif
}
ll ans = 1;
int n,k,cnt;
int a[maxn],b[maxn>>1];
ll same[maxn>>1][2],diff[maxn>>1][2];
void init(){
same[0][1] = diff[0][0] = 1;
for(int i = 1;i <= n>>1;i++){
same[i][0] = ((k-2)*same[i-1][0] + (k-1)*same[i-1][1])%mod;
same[i][1] = same[i-1][0];
diff[i][0] = ((k-2)*diff[i-1][0] + (k-1)*diff[i-1][1])%mod;
diff[i][1] = diff[i-1][0];
}
}
void solve(){
for(int l = 1,r = l;l <= cnt;l = r){
while(r <= cnt && b[l] == b[r])r++;
int len = r-l;
if(b[l] != -1){
if(len > 1){
ans = 0;
break;
}
continue;
}
if(l == 1 && r == cnt+1){
ans *= k;
ans %= mod;
for(int j = 2;j <= cnt;j++)ans *= (k-1),ans %= mod;
}
else if(l == 1 || r == cnt+1){
for(int j = 1;j <= len;j++)ans *= (k-1),ans %= mod;
}
else ans *= b[l-1]==b[r]?same[len][0]:diff[len][0],ans %= mod;
}
}
int main(){
redirect();
scanf("%d %d",&n,&k);
init();
for(int i = 1;i <= n;i++)scanf("%d",&a[i]);
for(int i = 1;i <= n;i+=2)b[++cnt] = a[i];
solve();
cnt = 0;
for(int i = 2;i <= n;i+=2)b[++cnt] = a[i];
solve();
printf("%I64d\n",ans);
return 0;
}