P1972 [SDOI2009] HH的项链

P1972 [SDOI2009] HH的项链

我们考虑将所有询问按照右端点归类。

然后从左往右扫描每个位置,如果前面有位置和它重复,就把前面的位置删掉(这样做是对的,因为右端点只可能在之后了,那么要访问到前面的位置,就必须要到达这个位置,相当于把重复的贡献减掉)。

初始时假设所有位置都不重复,都是 \(1\)

每次操作只可能将某个位置减少 \(1\),或者区间查询。

可以使用树状数组。

而且建树也可以直接将 \(c_i\leftarrow \text{lowbit}(c_i)\),做到 \(O(n)\) 建树。

复杂度 \(O(n+m)\log n\)(线段树常数大)。

// Problem: P1972 [SDOI2009] HH的项链
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1972
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<cstring>
#include<cstdio>
const int N=1000010,M=4*N;
char num[10];
int cnt,c[N],n,a[N],h[N],e[N],ne[N],idx,ans[N],id[N],p[N];
void add(int a,int b,int c){
	e[idx]=b,id[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void read(int &x){
	x=0;
	char c=getchar();
	while(c<'0'||c>'9')c=getchar();
	while(c>='0'&&c<='9'){
		x=x*10+c-48;
		c=getchar();
	}
}
void write(int x){
	int cur=0;
	while(x)num[cur++]=x%10+'0',x/=10;
	while(cur--)putchar(num[cur]);
	putchar('\n');
}
void sub(int x){
	for(;x<=n;x+=x&-x)--c[x];
}
int sum(int x){
	int res=0;
	for(;x;x-=x&-x)res+=c[x];
	return res;
}
int main(){
	read(n);
	memset(h+1,-1,n*4);
	for(int i=1;i<=n;++i)read(a[i]);
	int m;
	read(m);
	for(int i=1;i<=m;++i){
		int l,r;
		read(l),read(r);
		add(r,l,i);
	}
	for(int i=1;i<=n;++i)c[i]=i&-i;
	for(int i=1;i<=n;++i){
		int x=a[i];
		if(p[x])sub(p[x]);
		p[x]=i;
		for(int j=h[i];~j;j=ne[j])ans[id[j]]=sum(i)-sum(e[j]-1);
	}
	for(int i=1;i<=m;++i)write(ans[i]);
	return 0;
}
posted @ 2023-11-17 12:57  wscqwq  阅读(8)  评论(0编辑  收藏  举报