Educational Codeforces Round 62 (Rated for Div. 2) E. Palindrome-less Arrays(DP+瞎搞)

题目链接
题意

给你一个长为 n n n 的数组,和一个值 k k k,你能改变 − 1 -1 1 1 − k 1-k 1k 中的任意值,求字串不是回文串(长度大于 1 1 1,且长度为奇数)的方案数,膜 998244353 998244353 998244353

思路
  • 第一步比较好想,如果一个串满足上述回文串,那么其长度为3的中心字串必定回文,所以只要使所有长度为3的子串不是回文串即满足。
  • 进一步简化,存在回文串,等价于,存在 i i i 满足 a i = = a i − 2 ∣ ∣ a i = = a i + 2 a_i == a_{i-2} || a_i == a_{i+2} ai==ai2ai==ai+2 ,那么我们可以奇偶分离判断相邻数字是否相同,最后两组方案数相乘即可,接下来对奇偶分离后的组进行分析,设长度为 n n n,能选的数一共 k k k 种。
  • 将相邻的 − 1 -1 1 全部提取一块计算,将所有块相乘即答案,单个块设存在 l e n len len − 1 -1 1
    • 全部为-1,即首位 k k k 种选择,其余全部 k − 1 k-1 k1 种选择,即 a n s = k ∗ ( k − 1 ) n − 1 ans = k*(k-1)^{n-1} ans=k(k1)n1
    • 单方向限制,左边或右边没有限制, a n s = ( k − 1 ) l e n ans = (k-1)^{len} ans=(k1)len
    • 数字两边不同、数字两边相同,预处处理 d p [ 是 否 相 同 ] [ 长 度 ] dp[是否相同][长度] dp[][],详见代码 d p [ 0 ] dp[0] dp[0] 表示相同, d p [ 1 ] dp[1] dp[1] 表示不同
代码
#include <bits/stdc++.h>
using namespace std;

#define ll long long

ll read(ll &a){return scanf("%lld",&a);}
ll rd(){ll tmp; scanf("%lld",&tmp); return tmp;}

ll mod = 998244353;
ll dp[2][200005], v[200005], m1[200005], tot1 = 0, m2[200005], tot2 = 0, n, k;

ll qpow(ll a, ll b)
{
    ll tmp = 1;
    while(b)
    {
        if(b&1) tmp = tmp*a%mod;
        a = a*a%mod;
        b >>= 1;
    }
    return tmp;
}

ll f(ll wtf[], ll len)
{
    for(ll i = 2; i <= len; ++i) if(wtf[i] == wtf[i-1] && wtf[i] != -1) return 0ll;
    ll le = 0, ans = 1;
    for(ll i = 1; i <= len; ++i)
    {
        if(wtf[i] != -1 || i == len)
        {
            if(i == len && wtf[i] == -1) ++i;
            if(le == 0 && i == len+1) return k*qpow(k-1,len-1)%mod;
            if(le == 0 || i == len+1) ans = ans*qpow(k-1,i-1-le)%mod;
            else if(wtf[le] == wtf[i]) ans = ans*dp[0][i-le-1]%mod;
            else ans = ans*dp[1][i-le-1]%mod;
            le = i;
        }
    }
    return ans;
}

int main()
{
    n = rd(), k = rd();
    for(ll i = 1; i <= n; ++i) read(v[i]);
    dp[0][0] = dp[1][0] = 1;
    for(ll i = 1; i <= n; ++i)
    {
        dp[0][i] = (k-1)*dp[1][i-1]%mod;
        dp[1][i] = ((i > 1ll ? dp[0][i-1] : 0)+max(0ll,k-2)*dp[1][i-1]%mod)%mod;
    }
    for(ll i = 1; i <= n; i += 2) m1[++tot1] = v[i];
    for(ll i = 2; i <= n; i += 2) m2[++tot2] = v[i];
    printf("%lld\n",f(m1,tot1)*f(m2,tot2)%mod);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值