Educational Codeforces Round 118 (Rated for Div. 2) D. MEX Sequences题解

Educational Codeforces Round 118 (Rated for Div. 2) D. MEX Sequences题解,第1张

Educational Codeforces Round 118 (Rated for Div. 2) D. MEX Sequences题解

题解

题目大意是求mex-correct子序列个数

我们先来看一下mex-correct子序列的性质

假设要在一个已经是mex-correct的子序列后面添加一个数

设当前子序列结尾为x,那么由定义知mex值为x-1或x+1

当mex=x+1时,接下来添加的数只能为x或x+1(后续无限制)

当mex=x-1时,接下来添加的数只能为x或x-2(且后续添加的数都只能为x或x-2)

于是我们大致得知了mex-correct子序列的增减情况

我们可以把子序列分成两半来计数

 

 所有子序列的答案都在红色点位置处进行统计

先计算前半段的答案,即每次只递增1或不变的子序列的个数

可以O(n)扫描一遍计算出来

对于后半段的答案,可以从后往前进行扫描,统计每个数的出现次数

每次的x-2只能在前方有x的时候才能选择

所以再用一个tmp数组,每次出现x时,强制选择当前x,计算答案,累加到之前的答案当中

最终的答案为前半段数量乘后半段的数量

注意前半段数量在反向扫描时还要进行回退 *** 作

如果预处理了2的n次幂可以做到O(n)

(博主太懒了,只用了ksm)

 代码:

#include
#include
#include
using namespace std;
const int mod=998244353;
const int inv2=(mod+1)/2;
int a[500055],tong[500055],cnt[500055],f[500055];
int ksm(int x,int y)
{
	int ret=1;
	while(y){
		if(y&1)ret=1ll*ret*x%mod;
		y>>=1;x=1ll*x*x%mod;
	}
	return ret;
}
int main()
{
	int T,n,i;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(i=1;i<=n;i++){
			scanf("%d",&a[i]);
			a[i]++;
		}
		for(i=0;i<=n+10;i++)f[i]=tong[i]=cnt[i]=0;
		tong[0]=1;
		int ans=0,tmp;
		for(i=1;i<=n;i++){
			tmp=(tong[a[i]-1]+tong[a[i]])%mod;
			tong[a[i]]=(tong[a[i]]+tmp)%mod;
			(ans+=tmp)%=mod;
		}
		for(i=n;i>=1;i--){
			tong[a[i]]=(1ll*tong[a[i]]-tong[a[i]-1]+mod)%mod;
			tong[a[i]]=1ll*inv2*tong[a[i]]%mod;
			ans=(ans+1ll*(tong[a[i]]+tong[a[i]-1])%mod*f[a[i]+2]%mod)%mod;
			cnt[a[i]]++;
			f[a[i]]=(ksm(2,cnt[a[i]]+(a[i]>=2?cnt[a[i]-2]:0)-1)+f[a[i]])%mod;
		}
		ans=(ans+ksm(2,cnt[2])-1)%mod;
		printf("%dn",(ans+mod)%mod);
	}
}

 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5635148.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存