题解:P10264 [GESP202403 八级] 接竹竿

考虑暴力。

思路

预处理出每一个数的后继(即下一个出现的位置) nxt\texttt{nxt} 数组。

对于每一个询问区间枚举,如果发现枚举的数的后继在区间内,那么就跳过这个区间。

可以这么理解:因为当你在后面枚举到它的后继的时候,你就要把它和它的后继之间的所有数全部拿走。拿走的部分不应统计出答案。

例如 1 1 6 4 5 1 4 1

  1. 我们读入 1。序列为 1
  2. 我们又读入 1,序列为 1 1,需要消除,消除后序列为空。
  3. 依次读入 6 4 5 1,序列为 4 5 1
  4. 读入 4,序列为 4 5 1 4,消除后变为 6
  5. 读入 1,无法再进行匹配,序列变为 1 6

可以发现,在从前向后枚举的过程中,枚举到的数和它后继之间的数都要被消除掉(例如 4 5 1 4)。因此该算法正确。

核心代码如下:

while(q--) {
			l=read(),r=read();
			int ans=0;
			for(int i=l; i<=r; i++) {
				if(nxt[i]<=r) {// nxt 数组即预处理出的后继
					i=nxt[i];// 跳过这个区间
                    // 因为执行完 i=nxt[i] 操作后会进行 i++ 操作,所以下一次进入循环是 nxt[i]+1 的值。
				} else ans++;
			}
			printf("%d\n",ans);
}

复杂度分析

因为 n13n\le 13,所以每 1313 个数就会有一个重复。

即均摊下来复杂度最大可以达到 5×(1.5×104)×(1.5×104)13\dfrac{5\times (1.5\times 10^4)\times (1.5\times 10^4)}{13} 大致为 86538461.586538461.58.6×1078.6\times 10^7 的时间复杂度,加上快读和 O2\tt{O_2} 可以通过。

完整代码如下。

#include<bits/stdc++.h>
using namespace std;
int a[15005],pre[15005],nxt[15005],last[15];
int n,q,l,r,T;
inline int read() {
	int x=0,y=1;
	char c=getchar();
	while(c>'9'||c<'0') {
		if(c=='-')y=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=x*10+c-'0';
		c=getchar();
	}
	return x*y;
}
int main() {
	T=read();
	while(T--) {
		n=read();
		for(int i=1; i<=n; i++)a[i]=read();
		memset(last,0,sizeof(last));
		for(int i=1; i<=n; i++) {
			pre[i]=last[a[i]];
			last[a[i]]=i;
		}
		for(int i=1; i<=13; i++)last[i]=n+1;
		for(int i=n; i>=1; i--) {
			nxt[i]=last[a[i]];
			last[a[i]]=i;
		}
//	for(int i=1;i<=n;i++)cout<<nxt[i]<<' ';
		q=read();
		while(q--) {
			l=read(),r=read();
			int ans=0;
			for(int i=l; i<=r; i++) {
				if(nxt[i]<=r) {
					i=nxt[i];
				} else ans++;
			}
			printf("%d\n",ans);
		}
	}

	return 0;
}
posted @   Weslie_qwq  阅读(11)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示