【洛谷P1972】HH的项链

题目

题目链接:https://www.luogu.com.cn/problem/P1972
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。
有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答…… 因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。

思路

之前写的是莫队的\(O(n\sqrt{n})\)的算法。现在数据又加强了,于是回来写了一个\(O(n\log n)\)的算法。
考虑对于一堆\(r_i=x\)的询问\(i\),我们发现,如果一个数字在\([1,x]\)中出现了多次,那么取最大的且不超过\(x\)位置的数字显然是最优的。
那么我们在处理一堆右端点相同的询问时,我们只要知道在\([1,x]\)中每个数字出现位置最后的位置在哪里即可。
建立一棵树状数组,处理到\(x=i\)时,树状数组中位置\(j\)表示在区间\([1,i]\)中,位置\(j\)上的数字是否是最后出现的(所以有\(c[i]\in \{0,1\}\))。那么此时对于一个询问\([l,x]\),答案就是\(\sum^{x}_{i=l}c[i]\)。树状数组\(O(\log n)\)搞定。
在从位置\(i\)的询问转移到位置\(i+1\)的询问时,我们将\(a[i+1]\)上一次出现的位置在树状数组中清零,在位置\(i+1\)标记为1。仍然\(O(\log n)\)
时间复杂度\(O(n\log n)\)

代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;

const int N=1000010;
int n,m,a[N],last[N],ans[N];

struct ASK
{
	int l,r,id;
}ask[N];

struct BIT
{
	int c[N];
	
	int ask(int x)
	{
		int sum=0;
		for (int i=x;i;i-=i&-i)
			sum+=c[i];
		return sum;
	}
	
	void add(int x,int val)
	{
		if (!x) return;
		for (int i=x;i<=n;i+=i&-i)
			c[i]+=val;
	}
}bit;

bool cmp(ASK x,ASK y)
{
	return x.r<y.r;
}

int read()
{
	int d=0,f=1; char ch=getchar();
	while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d*f;
}

int main()
{
	n=read();
	for (int i=1;i<=n;i++)
		a[i]=read();
	m=read();
	for (int i=1;i<=m;i++)
		ask[i].l=read(),ask[i].r=read(),ask[i].id=i;
	sort(ask+1,ask+1+m,cmp);
	for (int i=1;i<=m;i++)
	{
		for (int j=ask[i-1].r+1;j<=ask[i].r;j++)
			bit.add(last[a[j]],-1),bit.add(j,1),last[a[j]]=j;
		ans[ask[i].id]=bit.ask(ask[i].r)-bit.ask(ask[i].l-1);
	}
	for (int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/12207743.html