zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 62 (Rated for Div. 2)E(染色DP,构造,思维,组合数学)

    #include<bits/stdc++.h>
    using namespace std;
    const long long mod=998244353;
    long long f[200007][2],g[200007][2];
    long long a[200007],b[200007],c[200007];
    int n,k,cnt1,cnt2;
    long long qpow(long long a,long long p){
        long long ans=1;
        while(p){
            if(p&1)
                ans=ans*a%mod;
            a=a*a%mod;
            p>>=1;
        }
        return ans;
    }
    long long solve(long long *a,int n,int k){
        for(int i=2;i<=n;i++)
            if(a[i]==a[i-1]&&a[i]!=-1)//一定有奇数回文串,无法构造出good串
                return 0;
        int p=0;
        for(int i=1;i<=n;i++)
            if(a[i]!=-1)
                p++;
        if(!p)
            return qpow(k-1,n-1)*k%mod;//第一个随意选,剩下的选择和前一个不同的
        else if(p==1)
            return qpow(k-1,n-1);//选的和相邻的不同的
        else{
            if(p==n)
                return 1;//没有构造余地,全部已经安排好了
            if(k==1)//构造不出,一定会存在1?1只羊这样的回文串
                return 0;
            long long res=1;
            int l=0,r=0;
            for(int i=1;i<=n;i++){
                if(a[i]!=-1){
                    l=i+1;
                    break;
                }
                res=res*(k-1)%mod;//-1只要构造和相邻的不同即可
            }
            for(int i=n;i;i--){
                if(a[i]!=-1){
                    r=i;
                    break;
                }
                res=res*(k-1)%mod;//-1只要构造和相邻的不同即可
            }
            long long tmp=0;
            long long last=a[l-1];//上一个已经确定的a[i]
            for(int i=l;i<=r;i++){
                if(a[i]!=-1){
                    if(!tmp){//前一个也已经确定
                        last=a[i];//更新最新一个确定的a[i]后继续
                        continue;
                    }
                    if(a[i]!=last)//两端数字不同
                        res=res*f[tmp][0]%mod;
                    else//两端数字相同
                        res=res*g[tmp][0]%mod;
                    tmp=0;//归零
                    last=a[i];//更新
                }
                else
                    tmp++;//增加中间-1的长度
            }
            return res;
        }
    }
    int main(){
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        //f和g类似染色,预处理出除了两端不是-1中间全都是-1能有多少种安排情况
        f[1][0]=k-2;//表示两端数字不同时,第一维和结尾不同的方案数,k-2因为要和两边都不同
        f[1][1]=1;//表示两端数字不同时,第一维和结尾相同的方案数,1因为下一个就可以在和上一个相同的那一段中间插入一个
        g[1][0]=k-1;//表示两端数字相同时,第一维和结尾不同的方案数,k-1因为两边是相同的
        g[1][1]=0;//表示两端数字相同时,第一维和结尾相同的方案数,0因为下两个才可以在最初两边都插一个
        for(int i=2;i<=n/2+1;i++){//染色DP,分开讨论,每次在前面的情况后面加上一个
            f[i][0]=(f[i-1][0]*(k-2)%mod+f[i-1][1]*(k-1)%mod)%mod;
            f[i][1]=f[i-1][0];
            g[i][0]=(g[i-1][0]*(k-2)%mod+g[i-1][1]*(k-1)%mod)%mod;
            g[i][1]=g[i-1][0];
        }
        for(int i=1;i<=n;i+=2)
            b[++cnt1]=a[i];//分离出下标为奇数的a[i]
        for(int i=2;i<=n;i+=2)
            c[++cnt2]=a[i];//分离出下标为偶数的a[i]
        long long ans=solve(b,cnt1,k);
        ans=ans*solve(c,cnt2,k)%mod;
        printf("%lld",ans);
        return 0;
    }

    保持热爱 不懈努力 不试试看怎么知道会失败呢(划掉) 世上无难事 只要肯放弃(划掉)
  • 相关阅读:
    C# 上传辅助方法
    C# 经纬度计算
    C#Excel导出反射数据集
    C# 数据模板导出
    C# 百度经纬度获取地址信息
    【平台开发】— 5.后端:代码分层
    【平台开发】— 4.mysql建库建表
    【平台开发】— 3.前端开发思路
    【平台开发】— 2.前端:vue-element-admin
    【平台开发】— 1.开篇
  • 原文地址:https://www.cnblogs.com/ldudxy/p/10597534.html
Copyright © 2011-2022 走看看