codeforces 1140E Palindrome-less Arrays

考场上想得太简单了

很显然,奇数位和偶数位上的分开后,相邻位置的数字不相同就是good了

然后考场上直接乘法原理了。。。

其实不然,因为1 -1 -1 1这种跟2 -1 -1 1这种答案是不相同的,但是我考场上没考虑到就把样例全过了。

所以需要讨论,奇数位偶数位分开后,单独对其中一条数列讨论,分为左右都没有限制,只有左边有限制,只有右边,两边都有但数字不同,两边都有单数字相同这5种情况,分别对每种情况求出所有长度的答案。

右边没有限制的很简单,直接每次*(k-1)就行了。

右边有限制,我们只需要考虑,每一位上,假如没有限制,总共有sum个情况,跟右边的限制相等的有cnt个情况

那么当前长度的答案就是sum-cnt,因为当前长度的最后一个不能等于右边的限制。

其余的类似这样的递推,左边的限制只影响最初的sum和cnt的大小。

#include<bits/stdc++.h>
#define maxl 200010
using namespace std;

const int mod=998244353;

int n,k,cnt1,cnt2;
long long ans;
int a[maxl],od[maxl],ev[maxl];
long long f00[maxl],f01[maxl],f10[maxl],f11s[maxl],f11d[maxl];

inline void prework()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(i&1) od[++cnt1]=a[i];
		else	ev[++cnt2]=a[i];
	}
	
	f00[1]=k;
	for(int i=2;i<=n;i++)
		f00[i]=f00[i-1]*(k-1)%mod;
		
	long long sum=k,cnt=1,t1,t2;	
	f01[1]=sum-cnt;
	for(int i=2;i<=n;i++)
	{
		t1=sum*(k-1)%mod;
		t2=((sum-cnt)%mod+mod)%mod;
		f01[i]=((t1-t2)%mod+mod)%mod;
		sum=t1;cnt=t2;
	}
	
	f10[1]=(k-1);
	for(int i=2;i<=n;i++)
		f10[i]=f10[i-1]*(k-1)%mod;
	
	sum=k-1,cnt=0;
	f11s[1]=k-1;
	for(int i=2;i<=n;i++)
	{
		t1=sum*(k-1)%mod;
		t2=((sum-cnt)%mod+mod)%mod;
		f11s[i]=((t1-t2)%mod+mod)%mod;
		sum=t1;cnt=t2;
	}
	
	sum=k-1,cnt=1;
	f11d[1]=k-2;
	for(int i=2;i<=n;i++)
	{
		t1=sum*(k-1)%mod;
		t2=((sum-cnt)%mod+mod)%mod;
		f11d[i]=((t1-t2)%mod+mod)%mod;
		sum=t1;cnt=t2;
	}
}

inline void mainwork()
{
	ans=1;
	for(int i=1;i<=n;i++)
	if(a[i]>0)
	{
		if(i+2<=n && a[i+2]>0 && a[i+2]==a[i])
		{
			ans=0;
			return;
		}
	}
	int l=0,r=0;
	while(l<=cnt1 && r<=cnt1)
	{
		do
			l++;
		while(od[l]!=-1 && l<=cnt1);
		if(l>cnt1) break;
		r=l;
		while(od[r+1]==-1 && r<=cnt1)
			r++;
		if(l-1==0)
		{
			if(r==cnt1)
				ans=ans*f00[cnt1]%mod;
			else
				ans=ans*f01[r-l+1]%mod;
		}
		else if(r==cnt1)
			ans=ans*f10[r-l+1]%mod;
		else
		{
			if(od[l-1]==od[r+1])
				ans=ans*f11s[r-l+1]%mod;
			else
				ans=ans*f11d[r-l+1]%mod;
		}
		l=r+1;
	}
	l=0;r=0;
	while(l<=cnt2 && r<=cnt2)
	{
		do
			l++;
		while(ev[l]!=-1 && l<=cnt2);
		if(l>cnt2) break;
		r=l;
		while(ev[r+1]==-1 && r<=cnt2)
			r++;
		if(l-1==0)
		{
			if(r==cnt2)
				ans=ans*f00[cnt2]%mod;
			else
				ans=ans*f01[r-l+1]%mod;
		}
		else if(r==cnt2)
			ans=ans*f10[r-l+1]%mod;
		else
		{
			if(ev[l-1]==ev[r+1])
				ans=ans*f11s[r-l+1]%mod;
			else
				ans=ans*f11d[r-l+1]%mod;
		}
		l=r+1;
	}
}

inline void print()
{
	printf("%lld",ans);
}

int main()
{
	prework();
	mainwork();
	print();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值