SPOJ3267——D-query(主席树)

题意:

输入N个数字,查询区间[L,R]中有多少个不同的数字

思路:
主席树

每个位置上保存一个值。保存每个数的位置信息,如果这个数没有出现过,当前位置+1,如果出现过,将之前出现的位置-1,当前位置+1

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 30000+5;
int cnt;
int Tree[maxn*20], root[maxn*20];
int lson[maxn*20], rson[maxn*20];
int last[1000005];

void Build(int& rt, int l, int r){
	rt = ++cnt; Tree[rt] = 0;
	if(l == r) return;
	int mid = (l+r) >> 1;
	Build(lson[rt], l, mid);
	Build(rson[rt], mid+1, r);
}

void update(int& new_rt, int old_rt, int l, int r, int pos, int val){
	new_rt = ++cnt;
	lson[new_rt] = lson[old_rt]; rson[new_rt] = rson[old_rt];
	Tree[new_rt] = Tree[old_rt] + val;
	if(l == r) return;
	int mid = (l+r) >> 1;
	if(pos <= mid) update(lson[new_rt], lson[old_rt], l, mid, pos, val);
	else update(rson[new_rt], rson[old_rt], mid+1, r, pos, val);
}

int Query(int rt, int la, int rb, int l, int r){
	if(la <= l&&rb >= r) return Tree[rt];
	int mid = (l+r) >> 1;
	int ans = 0;
	if(la <= mid) ans += Query(lson[rt], la, rb, l, mid);
	if(rb > mid)  ans += Query(rson[rt], la, rb, mid+1, r);
	return ans;
}


int main()
{
	int n,m,x,y;
	while(scanf("%d",&n) == 1&&n){
		memset(last, 0, sizeof(last));
		cnt = 0;
		Build(root[0], 1, n);
		for(int i = 1; i <= n; ++i){
			scanf("%d", &x);
			update(root[i], root[i-1], 1, n, i, 1);
			if(last[x])
				update(root[i], root[i], 1, n, last[x], -1);
			last[x] = i;
		}
		//for(int i = 1; i <= 30; ++i) printf("%d ",Tree[i]);
		scanf("%d", &m);
		while(m--){
			scanf("%d%d",&x,&y);
			printf("%d\n", Query(root[y], x, y, 1, n));
		}
	}
	fclose(stdin);
	return 0;
}

离线树状数组:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 30000+5;
const int maxq = 200000+5;

int A[maxn], last[1000000+5], ans[maxq];
struct query{
	int l,r, id;
	bool operator < (const query& rhs) const{
		if(r != rhs.r) return r < rhs.r; 
		return l < rhs.l;
	}
}Q[maxq];

int C[maxn];
inline int lowbit(int x){return x & -x;}
void add(int x, int v){for(int i = x; i <= maxn; i+= lowbit(i)) C[i]+= v;}
int sum(int x){
	int ans = 0;
	for(int i = x; i > 0; i-= lowbit(i)) ans+= C[i];
	return ans;
} 

int main()
{
	int n, m;
	while(scanf("%d",&n) == 1&&n){
		
		for(int i = 1; i <= n; ++i) scanf("%d", &A[i]);
		scanf("%d", &m);
		for(int i = 1; i <= m; ++i) scanf("%d%d",&Q[i].l,&Q[i].r), Q[i].id = i;
		sort(Q+1, Q+m+1);
		
		int pre = 1;
		for(int i = 1; i <= m; ++i){
			for(int j = pre; j <= Q[i].r; ++j){
				add(j, 1);
				if(last[A[j]]) add(last[A[j]], -1);
				last[A[j]] = j;
				ans[Q[i].id] = sum(Q[i].r) - sum(Q[i].l-1);
			}
			pre = Q[i].r;
		}
		for(int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
	}
	fclose(stdin);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值