Educational Codeforces Round 62 (Rated for Div. 2) E. Palindrome-less Arrays dp 计数 组合数学

12 篇文章 0 订阅
5 篇文章 0 订阅

题目链接:https://codeforces.com/contest/1140/problem/E

题目大意

给定一个串,不能出现长度为奇数 ( l e n ≥ 3 ) (len\geq3) (len3)的回文串, − 1 -1 1的位置可以填 1 − k 1-k 1k的所有数,问有多少种合法的方案,输出方案数。

题解思路

思路来源于官方题解,看完就是恍然大悟的感觉。
不能出现长度为奇数 ( l e n ≥ 3 ) (len\geq3) (len3)的回文串 = = =没有长度为 3 3 3的回文串
这样就可以分奇偶两个串,相邻的两个数不相同。

通过枚举可以发现,一个连续的 − 1 -1 1串对答案的贡献只和 k k k以及左右两个数是否相等有关,和左右两个数的具体值无关

我用 d p [ l e n ] [ 0 / 1 ] dp[len][0/1] dp[len][0/1]表示长度为 l e n len len − 1 -1 1串对答案的贡献, 0 0 0表示串左右两个数相同 1 1 1表示左右两个数不同

比较懒,应该写个函数的,直接复制了一遍
在这里插入图片描述
这是我做的时候随便写的样例,可以发现:
d p [ i ] [ 1 ] dp[i][1] dp[i][1]的组成来源于上一长度中在 [ 1 , k ] [1,k] [1,k]中不为当前数的状态和
就是 d p [ i − 1 ] [ 0 ] ∗ ( k − 1 ) dp[i-1][0]*(k-1) dp[i1][0](k1)
d p [ i ] [ 0 ] dp[i][0] dp[i][0]的组成来源于上一长度中在 [ 1 − k ] [1-k] [1k]中不为当前数的状态和
就是 d p [ i − 1 ] [ 1 ] + d p [ i − 1 ] [ 0 ] ∗ ( k − 2 ) dp[i-1][1]+dp[i-1][0]*(k-2) dp[i1][1]+dp[i1][0](k2)

另外还要单独考虑:
1.全为 − 1 -1 1的情况,第一个位置有 k k k种选择,后面的都有 k − 1 k-1 k1种选择,对答案的贡献就是 k ∗ ( k − 1 ) l e n − 1 k*(k-1)^{len-1} k(k1)len1
2.只有一边有约束的情况,临近约束的点有 k − 1 k-1 k1种选择,后面每个数都有 k − 1 k-1 k1种选择,对答案的贡献是 ( k − 1 ) l e n (k-1)^{len} (k1)len

每一部分的贡献都是乘在 a n s ans ans上的(相互独立)

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#define int ll
#define debug cout<<"fuck"<<endl;
#define pb  push_back
const int mod=(int)998244353;
const int maxn=(int)2e5+5;
int m[maxn];
int dp[maxn][2];
vector<int>a,b;
ll quick_pow_mod(ll a,ll b,ll c)
{
    ll res=1;
    while(b)
    {
        if(b & 1)
        {
            res=(res*a)%c;
        }
        a=(a*a)%c;
        b=b>>1;
    }
    return res;
}
int n,k;
void init()
{
    //0表示不同,1表示相同
    dp[1][1]=k-1;
    dp[1][0]=k-2;
    for(int i=2;i<maxn;i++)
    {
        dp[i][1]=(dp[i-1][0]*(k-1))%mod;
        dp[i][0]=(dp[i-1][1]+dp[i-1][0]*(k-2))%mod;
    }
}
signed main()
{
    IOS
    bool fail=0;
    cin>>n>>k;
    init();
    /*for(int i=1;i<=5;i++)
    {
        cout<<dp[i][0]<<' '<<dp[i][1]<<endl;
    }*/
    for(int i=1;i<=n;i++)
    {
        cin>>m[i];
        if(i%2)a.pb(m[i]);
        else b.pb(m[i]);
        if(m[i]!=-1&&i>=3&&m[i]==m[i-2])
        {
            fail=1;
        }
    }
    if(fail==1)
    {
        cout<<0<<endl;
        return 0;
    }
    int flag;
    ll ans=1;
    for(int i=0;i<a.size();i++)
    {
        int len=0;
        if(a[i]==-1&&i==0)
        {
            len=1;
            while(a[i+1]==-1&&i+1<a.size())
            {
                i++;
                len++;
            }
            if(len==a.size())
            {
                ans*=k*quick_pow_mod(k-1,len-1,mod);
                ans%=mod;
                break;
            }
            else
            {
                ans*=quick_pow_mod(k-1,len,mod);
                ans%=mod;
                continue;
            }
        }
        else if(a[i]==-1&&i>=1)
        {
            int pre=a[i-1];
            len=1;
            while(a[i+1]==-1&&i+1<a.size())
            {
                i++;
                len++;
            }
            if(i==a.size()-1)
            {
                ans*=quick_pow_mod(k-1,len,mod);
                ans%=mod;
            }
            else
            {
                if(a[i+1]==pre)
                {
                    ans*=dp[len][1];
                    ans%=mod;
                }
                else
                {
                    ans*=dp[len][0];
                    ans%=mod;
                }
            }
        }
    }
    for(int i=0;i<b.size();i++)
    {
        int len=0;
        if(b[i]==-1&&i==0)
        {
            len=1;
            while(b[i+1]==-1&&i+1<b.size())
            {
                i++;
                len++;
            }
            if(len==b.size())
            {
                ans*=k*quick_pow_mod(k-1,len-1,mod);
                ans%=mod;
                break;
            }
            else
            {
                ans*=quick_pow_mod(k-1,len,mod);
                ans%=mod;
                continue;
            }
        }
        else if(b[i]==-1&&i>=1)
        {
            int pre=b[i-1];
            len=1;
            while(b[i+1]==-1&&i+1<b.size())
            {
                i++;
                len++;
            }
            if(i==b.size()-1)
            {
                ans*=quick_pow_mod(k-1,len,mod);
                ans%=mod;
            }
            else
            {
                if(b[i+1]==pre)
                {
                    ans*=dp[len][1];
                    ans%=mod;
                }
                else
                {
                    ans*=dp[len][0];
                    ans%=mod;
                }
            }
        }
    }

    cout<<ans<<endl;


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值