SDOI2009 HH的项链

题目传送门

这题是一道很好的莫队树状数组题目。

首先,我们需要离线操作,把询问全存下来,然后按询问的右端点排序

排好序后,我们可以更改树状数组维护的区间,同时记录每一个元素最后出现的位置,因为我们是从左向右来回答每个询问,所以在右面的元素肯定比在左面的相同元素作用更大一些,所以我们在相同元素中只保存在我们当前维护的区间内且最靠右的一个就好了。

之后就可以用前缀和来处理答案了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lb i&-i
using namespace std;
int tree[1000010],n;
void add(int x,int k){
	for(int i=x;i<=n;i+=lb)
	  tree[i]+=k;
}
int sum(int x){
	int anss=0;
	for(int i=x;i;i-=lb)
	  anss+=tree[i];
	return anss;
}
struct zzz{
	int l,r,pos;
}q[200010];
bool cmp(zzz x,zzz y){
	if(x.r!=y.r) return x.r<y.r;
	else return x.l<y.l;
}
int read(){
	int k=0; char c=getchar();
	for(;c<'0'||c>'9';) c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  k=k*10+c-48;
	return k;
}
int a[500010],last[1000010],ans[200010];
int main(){
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	int m=read();
	for(int i=1;i<=m;i++)
	  q[i].l=read(),q[i].r=read(),q[i].pos=i;
	sort(q+1,q+m+1,cmp);
	int i=0;
	for(int j=1;j<=m;j++){
		while(i<q[j].r){  //i相当于一个指针,表示我们当前处理到哪
			i++;
			if(last[a[i]]){
				add(last[a[i]],-1); add(i,1); //及时更改
				last[a[i]]=i;
			}
			else{
				add(i,1); last[a[i]]=i;
			}
		}
		ans[q[j].pos]=sum(q[j].r)-sum(q[j].l-1); //处理询问
	}
	for(int i=1;i<=m;i++) printf("%d
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/morslin/p/11854751.html