CF1712C的题解

对于 n=1,答案显然为 0

我们能很清楚一点,因为 ai>0,所以当 ax 需要改为 0 时, a1ax1 也都必须改 0,这样才能使前面的满足 ai1ai

那我们首先得先记录每一个数出现的最后一个位置 last[a[i]],以及最后一个不满足 ai1ai 的位置 find

scanf("%d",&n);
for(int i=1;i<=n;i++)
{
	scanf("%d",&a[i]);
	last[a[i]]=i;
	if(a[i-1]>a[i]) find=i;
}
if(n==1)
{
	printf("0\n");
	continue;
}

然后我们用 lr 两个指针用类似于双指针的方法来找最后一个需要被改为 0 的数。

一开始,令 l=1,r=find-1

然后遍历该区间的数,如果其中有一个数最后出现的位置超过了该区间的右端点,那么我们枚举范围就要往后调。

l=1,r=find-1,tmp=0,flag=0;
while(!flag)
{
	flag=1;
	for(int i=l;i<=r;i++)
	{
		tmp=max(tmp,last[a[i]]);//找到最右边的数
		if(tmp>r) flag=0;//如果超过右端点,则打上标记
	}
	if(!flag) l=r+1,r=tmp;//如果超过,则更新左右端点,准备下一次枚举
}

最后只需要扫一遍 1r,找到其中有多少种不一样的数即可。

for(int i=1;i<=r;i++)
	if(!b[a[i]])
	{
		tot++;
		b[a[i]]++;
	}

code

#include<cstdio>
#include<cstring>
int t;
int n;
int a[100010];
int b[100010];
int last[100010];
bool flag;
int tot;
int find;
int tmp;
int l,r;
int max(int a,int b)
{
	return a>b?a:b;
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		memset(b,0,sizeof(b));
		tot=find=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			last[a[i]]=i;
			if(a[i-1]>a[i]) find=i;
		}
		if(n==1)
		{
			printf("0\n");
			continue;
		}
		l=1,r=find-1,tmp=0,flag=0;
		while(!flag)
		{
			flag=1;
			for(int i=l;i<=r;i++)
			{
				tmp=max(tmp,last[a[i]]);
				if(tmp>r) flag=0;
			}
			if(!flag) l=r,r=tmp;
		}
		for(int i=1;i<=r;i++)
			if(!b[a[i]])
			{
				tot++;
				b[a[i]]++;
			}
		printf("%d\n",tot);
	}
	return 0;
}

时间复杂度?

输入的时间复杂度显然为 O(n)

lr 是以上一个区间的结果来进行移动的,每次枚举的区间都不会重叠,所以 i 最坏情况下枚举的范围是 1n,时间复杂度也为 O(n)

r 最坏情况下为 n,时间复杂度显然也为 O(n)

综上,单次询问时间复杂度为 O(n),可以通过此题。

posted @   osfly  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示