bzoj 1878: [SDOI2009]HH的项链(主席树)

1878: [SDOI2009]HH的项链

Time Limit: 4 Sec   Memory Limit: 64 MB
Submit: 5317   Solved: 2624
[ Submit][ Status][ Discuss]

Description

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

Input

第一行:一个整数N,表示项链的长度。 
第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。 
第三行:一个整数M,表示HH询问的个数。 
接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
N ≤ 50000,M ≤ 200000。

Output

M行,每行一个整数,依次表示询问对应的答案。

Sample Input

6
1 2 3 4 3 5
3
1 2
3 5
2 6

Sample Output

2
2
4


据说当时出这道题的时候,还没有主席树这东西

现在有了主席树,就成水题了

记L[i]为第i个数字上一次出现的位置,那么对于每次询问[x, y],答案就是这个区间内满足L[i]<x的数的个数


#include<stdio.h>
#include<algorithm>
using namespace std;
typedef struct
{
	int l, r;
	int val;
}Tree;
Tree s[4000005];
int n, cnt, pos[1000005], L[100005], a[100005], t[100005];
int Build(int l, int r)
{
	int m, root;
	m = (l+r)/2;
	root = ++cnt;
	if(l==r)
	{
		s[root].val = 0;
		return root;
	}
	s[root].l = Build(l, m);
	s[root].r = Build(m+1, r);
	s[root].val = s[s[root].l].val+s[s[root].r].val;
	return root;
}
int Update(int root, int k)
{
	int now, temp, m, l, r;
	now = temp = ++cnt;
	l = 1, r = n+1;
	while(l<r)
	{
		m = (l+r)/2;
		s[now].val = s[root].val+1;
		if(k<=m)
		{
			s[now].l = ++cnt;
			s[now].r = s[root].r;
			root = s[root].l;
			r = m;
			now = cnt;
		}
		else
		{
			s[now].r = ++cnt;
			s[now].l = s[root].l;
			root = s[root].r;
			l = m+1;
			now = cnt;
		}
	}
	s[now].val = s[root].val+1;
	return temp;
}
int Query(int root, int l, int r, int a, int b)
{
	int ans, m;
	ans = 0;
	if(l>=a && r<=b)
		return s[root].val;
	m = (l+r)/2;
	if(a<=m)
		ans += Query(s[root].l, l, m, a, b);
	if(b>=m+1)
		ans += Query(s[root].r, m+1, r, a, b);
	return ans;
}
int main(void)
{
	int i, m, l, r;
	scanf("%d", &n);
	for(i=2;i<=n+1;i++)
	{
		scanf("%d", &a[i]);
		L[i] = max(pos[a[i]], 1);
		pos[a[i]] = i;
	}
	t[1] = Build(1, n+1);
	for(i=2;i<=n+1;i++)
		t[i] = Update(t[i-1], L[i]);
	scanf("%d", &m);
	while(m--)
	{
		scanf("%d%d", &l, &r);
		l += 1, r += 1;
		printf("%d\n", Query(t[r], 1, n+1, 1, l-1)-Query(t[l-1], 1, n+1, 1, l-1));
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值