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的个数。

扫描二维码关注公众号,回复: 11354750 查看本文章

    ③最后一位为-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;
}

猜你喜欢

转载自blog.csdn.net/krypton12138/article/details/89166698