P1972 [SDOI2009] HH的项链 题解

题目链接\color{#87E7F1}\small\textbf{题目链接}

题意\color{#FF80FF}\textbf{题意}:

给定长度为 NNaa 序列,求 MM 段区间不同数的个数。(1n,m,ai1061\le n,m,a_i \leq 10^6


观察数据范围,aia_i 的范围不是 10910^9 ,有极大可能以此来解题。

事实上,如果使用暴力,即使用前缀和。思路如下:

对于 aia_i

  • 未重复,直接加入。

  • 重复,如果直接加入,会导致 aia_i 被统计数次。可将 aj[1ji,ai=aj]a_j[1\leq j\leq i,a_i=a_j] 赋值为 00。前缀和此时会失效,需重新统计。因为只有 10610^6,可定义 visvis 数组。visivis_i:值为 iiaa 数组元素第一次出现的下标。

时间复杂度为 O(N2)O(N^2)

超时了。但观察以上结论,易得需要单点修改,区间查询。

树状数组可用 LogNLogN 完成上述操作。

再对答案离线处理,将区间对 rr 升序排序。在处理 rir_i 时,就可以继承前面。复杂度为 O(MLogN)O(MLogN)。最后将离线处理的数据按原顺序输出。

关于排序\color{#FF80FF}\textbf{关于排序}

如使用结构体,代码非常冗长。

此时可以定义 mpi=imp_i=i,此时 ympiy_{mp_i} 即为 yiy_i,对 mpmp 排序,但比较 ympiy_{mp_i}(等价 yiy_i),仍有对 yy 排序的效果。虽然 yiy_i 为原元素,但改变 mpimp_i,通过映射排序,此时 ympiy_{mp_i} 为以 yy 为关键字排序的 yiy_i,可以实现代码的简洁化。

#include<bits/stdc++.h>
using namespace std;
#define _x(i) x[mp[i]]
#define _y(i) y[mp[i]]
const int N=1e6+1;
int t[N],o,h,x[N],y[N],vis[N],s[N],mp[N],j=1,as[N],be[N];
int n,m,a[N];
int lowbit(int x) {
	return x & -x;
}
void Plus(int i,int q) {
	while(i<=n) {
		t[i]+=q;
		i+=lowbit(i);
	}
}
int ans(int r) {
	int sr=0;
	while(r>=1) {
		sr+=t[r];
		r-=lowbit(r);
	}
	return sr;
}
bool cmp(int p,int q) {
	return y[p]<y[q];
}
int main() {
	cin>>n;
	for(int i=1;i<=n;i++) {
		cin>>a[i];
	}
	cin>>m;
	for(int i=1;i<=m;i++) {
		scanf("%d%d",&x[i],&y[i]);
		mp[i]=i;
	}
	sort(mp+1,mp+m+1,cmp);
	for(int i=1;i<=m;i++) {
		while(j<=_y(i)) {
			if(vis[a[j]]) {
				Plus(vis[a[j]],-1);
			}
			vis[a[j]]=j;
			Plus(j,1);
			j++;
		}
		as[mp[i]]=ans(_y(i))-ans(_x(i)-1);
	}
	for(int i=1;i<=m;i++) {
		cout<<as[i]<<"\n";
	}
	return 0;
}
posted @ 2023-06-01 14:43  cjrqwq  阅读(24)  评论(0)    收藏  举报  来源