把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

[题解] HH的项链

题目链接

树状数组(或线段树)离线

我们可以维护一个树状数组 \(tree\) ,计算 \([1,i]\) 出现的不同种类的个数。然后根据前缀合思想,取 \(ans=query(r)-query(l-1)\)

离线按 \(r\) 排序查询区间。

由于我们要求不重复,所以靠右的比靠左的更有价值(更关心靠右的)所以我们每次遇到一个重复的就可以把原来的清掉(靠左的已经被放进靠左的答案中了)。

是否重复的判断可以通过一个数组 \(vis\) 实现,同时 \(vis\) 还可以维护更新同一个数靠右的位置

线段树应该同理,但是既然有树状数组做法,为什么不写树状数组呢?

#include<bits/stdc++.h>

using namespace std;

#define rg register
#define ll long long
#define ull unsigned long long

namespace Enterprise
{
	
	inline int read()
	{
		int s = 0,f = 0;
		rg char ch = getchar();
		while( !isdigit(ch) ) f |= (ch == '-'),ch = getchar();
		while( isdigit(ch) ) s = (s << 1) + (s << 3) + (ch ^ 48),ch = getchar();
		return f ? -s : s;
	}
	
	#define lowbit(x) ((x)&(-x))
	
	const int N=1e6+15;
	
	struct lyy
	{
		int l,r,id;
		inline bool operator < ( const lyy &rhs )const
		{
			return r < rhs.r;
		}
	};
	
	lyy op[N];
	
	int a[N],tree[N],ans[N],vis[N],n,m;
	
	inline void add(int x,int v)
	{
		for( ; x <= n ; x += lowbit(x) )
			tree[x]+=v;
	}
	
	inline int query(int x)
	{
		int ans=0;
		for( ; x ; x -= lowbit(x) ) ans += tree[x];
		return ans;
	}
	
	inline void main()
	{
		n = read();
		for( rg int i = 1 ; i <= n ; i++ ) a[i] = read();
		m = read();
		for( rg int i = 1 ; i <= m ; i++ ) op[i].l = read(),op[i].r = read(),op[i].id = i;
		sort(op + 1,op + m + 1);
		
		for( rg int i = 1,j = 1 ; i <= m ; i++ )
		{
			for( ; j <= op[i].r ; j++ )
			{
				if(vis[a[j]]) add(vis[a[j]],-1);
				add(j,1);
				vis[a[j]] = j;
			}
			ans[op[i].id] = query(op[i].r) - query(op[i].l - 1);
		}
		
		for( rg int i = 1 ; i <= m ; i++ ) printf("%d\n",ans[i]);
	}
}

signed main()
{
	Enterprise::main();
	return 0;
}
posted @ 2020-03-11 09:53  UssEnterprise  阅读(94)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end