E. Palindrome-less Arrays(思维dp、拆分序列、回文)

这里写目录标题

传送门

在这里插入图片描述

题解

分析
给出一个长度为n的数组,其中有若干个-1,需要用1~k中的数去替换所有的-1,使得数组序列中不存在长度为奇数(>3)的回文序列
2 3 2, 2 2 2,…1 2 3 2 1…,观察可发现,不存在长度为奇数的回文序列等价于 a [ i ] ! = a [ i + 2 ] , a [ i ] 与 a [ i + 1 ] a[i]!=a[i+2],a[i]与a[i+1] a[i]!=a[i+2]a[i]a[i+1]没有限制关系,这样一来可以把序列拆成奇数下标和偶数下标对应元素分别组成的两个子序列
最 后 的 方 案 数 = 奇 序 列 填 补 方 式 ∗ 奇 序 列 填 补 方 式 最后的方案数=奇序列填补方式*奇序列填补方式 =
那么对于子序列,该满足的条件就是 相邻元素不相等
如何选取1~k中的数去替换序列中所有的-1,以达到上述要求呢
2 -1 3 -1 -1 3 -1 -1 4
第1个-1选法k-2, (根据这个-1两段的数字,不能选2、3),第2个-1,乍一看不能确定,因为后面连着-1,那如果多个-1连成一块
又该如何确定方案数呢? 我们把连续的-1当成一块,因为这块-1的位置 会相互影响
先 看 一 块 中 有 2 个 负 1 , 第 1 个 负 1 有 k − 1 种 , 第 2 个 负 1 有 k − 2 种 先看一块中有2个负1,第1个负1有 k-1种,第2个负1有k-2种 21,11k121k2
而3 -1 -1 4 这块负1中,第1个负1有k-1种,第2个负1的种数就要分类讨论了
若 第 1 个 负 1 填 4 , 第 2 个 负 1 有 k − 1 种 ; 若 第 1 个 − 1 负 1 不 填 4 , 第 2 个 负 1 有 k − 2 种 若第1个负1填4, 第2个负1有k-1种 ;若第1个-1负1不填4, 第2个负1有k-2种 114,21k1;1114,21k2
需要分类讨论,-1块的规模一大就需要多重的分类讨论,于是想到dp递推
方案数取决于-1块两端的两个数是否相等

  • 如果相等, d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] ∗ ( k − 1 ) dp[i][1]=dp[i-1][0]*(k-1) dp[i][1]=dp[i1][0](k1)
    因为 3 -1 -1……-1 -1 3,最后一个-1有k-1种方案,剩余的连续的i-1个-1
    也就是一个长度为i-1的f-1块,首尾两段只能不相等,
    因为最后一个-1不可能取3
  • 如果不相等, d p [ i ] [ 0 ] = d p [ i − 1 ] [ 0 ] ∗ ( k − 2 ) + d p [ i − 1 ] [ 1 ] ∗ 1 dp[i][0]=dp[i-1][0]*(k-2)+dp[i-1][1]*1 dp[i][0]=dp[i1][0](k2)+dp[i1][1]1
    因为3 -1 -1……-1 -1 4首尾不相等,前面长度为i-1的-1块首尾两段
    既可能相等也可能不相等,相等时,最后一个-1必然取3
    不相等,最后一个-1有k-2种取法

(在状态表示中除了-1块的长度,多加一层表示两段是否相等)
完成该子序列的方式自然就是各块-1

//#include <bits/stdc++.h>
#include <iostream> 
using namespace std;
#define int long long
const int N=3e5+5;
const int mod=998244353; 
int n,k;
int w[N];
int a[N];//奇数下标对应的序列 
int b[N];//偶数下标 
int f[N][2];//长度为i的-1块 
int quickpow(int b,int e){
	b%=mod;
	e%=mod;
	int res=1;
	while(e){
		if(e&1)res=res*b%mod;
		b=b*b%mod;
		e>>=1;
	}
	return res;
}

int solve(int v[],int n){//n优先取的是形参而不是全局变量 
	if(!n)return 1;
	if(n==1){
		if(v[n]!=-1)return 1;
		else return k;
	}
	int res=0; 
//	int pre=0;//上一个非-1的数
//	v[0]=3e5;//一个不存在的数 ,不行,要特殊处理两端存在的-1
	int l=1,r=n;
	while(l<=n&&v[l]==-1){
		l++;//l最终指向第一个 不是-1的数 
	} 
	while(r>=1&&v[r]==-1){
		r--;//r最终指向最后一个 不是-1的数 
	} 
	if(l>n)return quickpow(k-1,n-1)*k%mod;//n个-1
//	l-1个-1,n-r个-1
	res=quickpow(k-1,l-1)*quickpow(k-1,n-r)%mod;//都受到一端的限制
	
	int pre=l; 
	for(int i=l+1;i<=r;i++){
		if(v[i]==-1)continue;
		else{//1 -1 -1 4
			int m=i-pre-1;//-1块的长度 
			res=res*f[i-pre-1][(v[pre]==v[i])]%mod;
			pre=i; 
		}
	}
	
	return res; 
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); 
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>w[i];
//	初始化-1块对应的方案数,-1块的长度至少为1 
	f[0][0]=1;//因为f[1][1]一定为k-1且f[1][1]要由f[0][0]推来 
	f[0][1]=0;//因为f[1][0]定为k-2且f[1][0]要由f[0][0]=1和f[0][0]推来 
	for(int i=1;i<=n;i++){
		f[i][1]=f[i-1][0]*(k-1)%mod;
		f[i][0]=(f[i-1][0]*(k-2)+f[i-1][1]*1)%mod; 
	}
	for(int i=1;i<=n;i++){
		if(i&1)a[++a[0]]=w[i];//a[0]做哨兵,最后存储的就是a数组长度
		else b[++b[0]]=w[i]; 
	} 
	cout<<solve(a,a[0])*solve(b,b[0])%mod;

	return 0;
}

另一种递推方式

算是另一种递推啦
待看做法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值