2018 牛客网暑期ACM多校训练营(第一场)J.Different Integers(树状数组+区间数字种数)

题目

n(n<=1e5)个数,第i个整数ai范围[1,n]

q(q<=1e5)个询问,每次给出Li,Ri,询问[1,Li]∪[Ri,n]中出现的数的种类

思路来源

凡神

题解

[1,l]和[r,n],先把数组复制一遍,

也就是[r,l+n]的答案了,排序后成为新的[L,R]后处理询问

对于L增序询问,初始L=1,

贪心地选择,选择值出现的第一个位置,越左越好

每删掉一个值的第一个位置(L大于这个位置),

就把这个值的下一个位置加到树状数组里

树状数组里对答案产生贡献的,始终是每个值出现的第一个位置

不在区间询问里的,自然作差就没了

所以需要倒序预处理,每个值出现的首位置和下一个出现的位置

代码

#include
using namespace std;
const int maxn=1e5+10;
int n,q,a[maxn*2];
int tree[maxn*2];
int head[maxn],nex[2*maxn];
int l,r; 
bool vis[maxn];
//head[a[i]]:a[i]这个值第一次出现的位置
//next[i]:与i位置相同的值下一次出现的位置 
int ans[maxn];
struct node
{
	int l,r,id;
	node(){} 
	node(int L,int R,int i):l(L),r(R),id(i){} 
}e[maxn];
bool operator<(node a,node b)
{
	return a.l0;i-=i&-i)
	ans+=tree[i];
	return ans;
}
int main()
{
	while(~scanf("%d%d",&n,&q))
	{
	 for(int i=1;i<=n;++i)
	 {
	  scanf("%d",&a[i]);
	  a[i+n]=a[i];
	  head[i]=2*n+1;
	  vis[i]=0;
     }
     for(int i=1;i<=q;++i)
	 {
		scanf("%d%d",&l,&r);
		e[i]=node(r,l+n,i);
	 }
	 n*=2;
     for(int i=n;i>=1;--i)
     {
      nex[i]=head[a[i]];
	  head[a[i]]=i;
	  tree[i]=0; 
     }
	 sort(e+1,e+q+1); 
	 int L=1;
	 for(int i=1;i<=n;++i)
	 if(!vis[a[i]])
	 {
	 	vis[a[i]]=1;
	 	add(i,1);
	 }
	 for(int i=1;i<=q;++i)
	 {
	 	while(L

 

你可能感兴趣的:(线段树(权值线段树)/树状数组)