AtCoder Beginner Contest 174 F - Range Set Query

Time Limit: 2 sec / Memory Limit: 1024 MB

Score : 600600 points

Problem Statement

We have NN colored balls arranged in a row from left to right; the color of the ii-th ball from the left is cici.

You are given QQ queries. The ii-th query is as follows: how many different colors do the lili-th through riri-th balls from the left have?

Constraints

  • 1≤N,Q≤5×1051≤N,Q≤5×105
  • 1≤ci≤N1≤ci≤N
  • 1≤li≤ri≤N1≤li≤ri≤N
  • All values in input are integers.

Input

Input is given from Standard Input in the following format:

NN QQ
c1c1 c2c2 ⋯⋯ cNcN
l1l1 r1r1
l2l2 r2r2
::
lQlQ rQrQ

Output

Print QQ lines. The ii-th line should contain the response to the ii-th query.


Sample Input 1 Copy

Copy

4 3
1 2 1 3
1 3
2 4
3 3

Sample Output 1 Copy

Copy

2
3
1
  • The 11-st, 22-nd, and 33-rd balls from the left have the colors 11, 22, and 11 - two different colors.
  • The 22-st, 33-rd, and 44-th balls from the left have the colors 22, 11, and 33 - three different colors.
  • The 33-rd ball from the left has the color 11 - just one color.

Sample Input 2 Copy

Copy

10 10
2 5 6 5 2 1 7 9 7 2
5 5
2 4
6 7
2 2
7 8
7 9
1 8
6 9
8 10
6 8

Sample Output 2 Copy

Copy

1
2
2
1
2
2
6
3
3
3

思路:树状数组模拟。

做法:首先将每个区间按右边界从小到大排序,对于每个区间,从左到右遍历颜色,每个颜色只保留一个,如果这个颜色有两个,那么就去掉左边的一个,保留右边的一个,最后计算区间的和,因为每个颜色只保留了一个,所以区间和就是种类数。

为什么要保留右边的:因为遍历是按照右边界从小到大的顺序排列,保留靠右的节点可以保证在即将出现的边界里只有被保留的节点。

例:1 2 3 4 1 2 3 

如果有两个区间 1 4 和 2 6 那么在第一个区间里,第一个1有意义,而在第二个区间里第一个1没有意义有,所以第二个1出现时应保留第二个1,并在树状数组里去除第一个1。

#include<iostream>
#include<cmath>
#include<algorithm>

#define ll long long

using namespace std;

const int N = 1e6 + 10;

int tr[N];
int n,m;
int last[N];
int ans[N];
struct node{
	int l,r,pos;
}nod[N];

int color[N];

int lowbit(int x)
{
	return x & -x;
}

void add(int x, int a)
{
	for(int i=x; i<=n; i+=lowbit(i)) tr[i]+=a;
}

int query(int x)
{
	int rul = 0;
	for(int i=x; i; i-=lowbit(i)) rul += tr[i];
	return rul;
}

bool cmp(node a, node b)
{
	return a.r < b.r;
}

int main()
{
	cin>>n>>m;
	for(int i=1; i<=n; i++) scanf("%d",&color[i]);
	for(int i=1; i<=m; i++)
	{
		scanf("%d %d",&nod[i].l,&nod[i].r);
		nod[i].pos = i;
	}
	sort(nod+1, nod+m+1, cmp);
	int next = 1;
	for(int i=1; i<=m; i++)
	{
		for(int j=next; j<=nod[i].r; j++)
		{
			if(last[color[j]]) add(last[color[j]], -1);
			last[color[j]] = j;
			add(j, 1);
		}
		next = nod[i].r+1;
		ans[nod[i].pos] = query(nod[i].r) - query(nod[i].l-1);
	}
	for(int i=1; i<=m; i++)
	{
		cout<<ans[i]<<endl;
	}
	return 0;
}

不明白的可以看下这道题的题解:https://www.luogu.com.cn/problem/P1972

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值