CodeForces - Educational Round 62 E. Palindrome-less Arrays dp

鏈接:

    E. Palindrome-less Arrays

題意:

    給一個長度爲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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章