CodeForces 1140E Palindrome-less Arrays(DP + 思维)

 

 

大致题意:给你一个序列,数字有-1或者1到k构成。其中的-1需要用1到k中任意一个数字替代,现在定义一个坏序列的标准是把-1替代完毕后,没有长度大于1的奇回文串。问你在所有的替代方案中,最后能得到不是坏序列的方案数是多少。

这里的一个奇回文串,看似很复杂不知道应该怎么做,但是其实很容易发现,所有的奇回文串必定包含一个长度为3的回文串。换句话说,含有长度为3的回文串的就是坏序列。再进一步,发现长度为3的回文串,第一个字符和第三个字符是一样的。也就意味着,如果我们把原串按照奇偶取拆成两个串的话,只要其中有一个串有连续两个相同的字符,那么这个串就是一个坏序列,反之就是好序列。

继续分析,最后的答案就是奇数串好序列个数*偶数串好序列个数。那么,现在问题的关键就是怎么求这个好序列个数。我们考虑dp[i][0]表示长度为i的一串-1且两端夹着两个不相等的数字的好序列方案数。同理,dp[i][1]表示长度为i的一串-1且两端夹着两个相等的数字的好序列的方案数。那么,我们可以很简单的得到转移方程:

                                 \large dp[i][0]=dp[i-1][0]+(k-2)*dp[i-1][1]

                                               \large dp[i][1]=(k-1)*dp[i-1][0]

                                                       \large dp[0][0]=0,dp[1][0]=1

根据这个转移方程,我们就可以把序列按照已知的数字进行分段,把每一段的方案数乘起来就是我们最后的答案。最后要特殊处理一下两端为-1的情况。具体见代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define file(x) freopen(#x".in","r",stdin);

using namespace std;
const int N = 2e5 + 10;
const int mod = 998244353;

int a[N],b[N];
int n,k,l1,l2;

LL solve(int *a,int len)
{
    if (len==1)
        if (a[1]==-1) return k;
                else return 1;
    LL sam=1,dif=0,res=1;
    int ls=a[1];
    for(int i=2;i<=len;i++)
    {
        LL nxt_sam=dif*(k-1)%mod;
        LL nxt_dif=(sam+(k-2)*dif%mod)%mod;
        if (a[i]==-1) sam=nxt_sam,dif=nxt_dif;
        else
        {
            if (ls==-1) res=res*(nxt_sam+nxt_dif*(k-1)%mod)%mod;
            else res=res*(a[i]==ls?nxt_sam:nxt_dif)%mod;
            ls=a[i]; sam=1; dif=0;
        }
    }
    res=res*(sam+dif*(k-1)%mod)%mod;
    if (ls==-1) return res*k%mod;
    return res;
}

int main()
{
    scc(n,k);
    for(int i=1;i<=n;i++)
    {
        int x; sc(x);
        if (i&1) a[++l1]=x;
            else b[++l2]=x;
    }
    printf("%lld\n",solve(a,l1)*solve(b,l2)%mod);
}
/*
5 3
-1 -1 -1 -1 -1
6 5
-1 -1 -1 2 -1 -1
9 3
-1 -1 -1 -1 1 2 3 -1 -1
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值