树状数组操作

[SDOI2009]HH的项链

题意:序列\(n <= 1e6\),有\(m <= n\)个询问,求\(l\)\(r\)中有几个不相同的数。
题解:如果\(n\)的范围为\(1e5\)就是莫队裸题,但是由于\(n\)的范围为\(1e6\)那么就是可用树状数组操作,具体就是可发现,只有最后出现的数字有价值,设\(r\)指针从左到右,如果出现了一个数之前没出现过,在树状数组里此位置\(+1\),如果当前\(r\)的数出现过,那么就是之前的位置在树状数组中\(-1\),然后新的位置\(+1\),然后询问的话,就是\(sum(r)-sum(l-1)\)就是区间\([l,r]\)的不同数的数量
代码:

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

using namespace std;
typedef long long ll;

const int N = 1000010,  M = 1000010, S = 1000010;
ll lowbit(ll x) {
	return -x&x;
}
int n;
struct BIT {
	int d[N], Pos[N];
	ll ask(ll pos) {ll ret = 0;while (pos>0) {ret += d[pos],pos -= lowbit(pos);}return ret;}
	void add(ll pos, ll add){while (pos <= n)d[pos]+=add,pos+=lowbit(pos);}
}bit;
int a[N];
struct query {
	int id,l, r;
}q[M];
ll ans[N];
bool cmp(query a, query b) {
	return a.r < b.r;
}
signed main() {
	scanf("%d",&n);
	for (int i = 1; i <= n; i++) 
	{
		scanf("%d", &a[i]);
	}
	int m;scanf("%d", &m);
	for (int i = 1; i <= m; i++) {
		int l, r;
		scanf("%d%d",&l,&r);
		q[i] = {i, l, r};
	}
	sort(q + 1, q + 1 +m, cmp);
//	for (int i = 1; i <= m; i++) {
//		cout << q[i].l << " " << q[i].r << endl;
//	}
	for (int i = 1, j = 1; i <= m; i++) {
		int id = q[i].id, l = q[i].l, r = q[i].r;
		
		while ( j <= r ) {
			int now = a[j];
			//cout << "j:" << j << " " << "now:" << now << "  " << bit.Pos[now] << endl;
			if (bit.Pos[now] == 0) {
				bit.add(j, 1);
				//cout << "add" << j << " 1\n";
				bit.Pos[now] = j;
			} else {
				bit.add(bit.Pos[now], -1);
				//cout << "add" << bit.Pos[now] << " " << -1 << endl;
				//cout << "add" << j << " 1\n";
				bit.add(j, 1);
				bit.Pos[now] = j;
			}
			//cout << bit.ask(j) << endl;
			j++;
		} 
		//cout << l << "->" << r << endl;
//		for (int ii = 0; ii <= n; ii++) {
//			cout <<"ii:" << ii << " " << bit.ask(ii) << endl;
//		}
		ans[id] = bit.ask(r) - bit.ask(l-1);
	//	cout << "ans" << " " << ans[id] << endl;
	}
	for (int i  =1;  i <= m; i++) {
		printf("%d\n", ans[i]);
	}
 	return 0;
} 
posted @ 2021-02-22 21:27  u_yan  阅读(45)  评论(0编辑  收藏  举报