当前位置: 首页 > 工具软件 > DQuery > 使用案例 >

【SPOJ - DQUERY】D-query【主席树 —— 区间中不同数的个数】

公良凯
2023-12-01

题意:

长度为 n n n的序列, q q q次询问,每次给出 l l l r r r,返回序列 [ l , r ] [l,r] [l,r]中不同数的个数。 ( 1 ≤ n ≤ 3 ∗ 1 0 4 , 1 ≤ q ≤ 2 ∗ 1 0 5 ) (1\leq n\leq 3*10^4,1\leq q\leq 2*10^5) (1n3104,1q2105)


思路:

与之前主席树的权值线段树思路不同,此题的思路是建立 n n n颗线段树,第 i i i颗线段树存储区间 [ 1 , i ] [1,i] [1,i]的信息。其中每个节点维护 s u m sum sum,表示节点对应区间中数的个数,因此每棵线段树中只保留每个数最后出现的位置。

举个例子,序列为 5   5   5   5   5 5\ 5\ 5\ 5\ 5 5 5 5 5 5,则第 1 1 1颗线段树中只有第一个位置 s u m sum sum 1 1 1,然后第二颗线段树从第一颗线段树继承过来,由于 5 5 5这个数字之前出现过,因此在第二颗线段树中令第一个位置的 s u m sum sum 0 0 0,令第二个位置的 s u m sum sum 1 1 1,来保存每个数字最后出现的位置。

因此查询区间 [ l , r ] [l,r] [l,r]时,就在第 r r r颗线段树中查询区间 [ l , n ] [l,n] [l,n]中数的个数即可。


代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 3*1e4+100;
const int M = 1e6+100;
const db EPS = 1e-9;
using namespace std;

int n,a[N],q;
int ls[40*N],rs[40*N],sum[40*N],root[N],sz;
int pos[M]; //记录每个数的位置

void build(int &now,int l,int r){
	if(!now) now = ++sz;
	if(l == r){
		sum[now] = 0; return;
	}
	build(ls[now],l,(l+r)>>1);
	build(rs[now],((l+r)>>1)+1,r);
	sum[now] = sum[ls[now]]+sum[rs[now]];
}

void update(int x,int &now,int l,int r,int p1,int ct){
	if(!now) now = ++sz;
	if(l == r){
		sum[now] = sum[x]+ct; return;
	}
	int mid = (l+r)>>1;
	if(p1 <= mid){
		if(!rs[now]) rs[now] = rs[x]; //继承前一个备份的节点
		if(ls[now] <= now) ls[now] = 0; //因为现在要修改ls[now], 因此要重新开空间
		//如果ls[now] <= now, 表示这个节点是之前继承的, 因此要赋为0, 重新开空间
		update(ls[x],ls[now],l,mid,p1,ct);
	}
	else{
		if(!ls[now]) ls[now] = ls[x];
		if(rs[now] <= now) rs[now] = 0;
		update(rs[x],rs[now],mid+1,r,p1,ct);
	}
	sum[now] = sum[ls[now]]+sum[rs[now]];
}

int ask(int &now,int l,int r,int p1){
	if(!now) now = ++sz;
	if(l >= p1) return sum[now];
	else if(r < p1) return 0;
	int mid = (l+r)>>1, ans = 0;
	if(mid >= p1) ans += ask(ls[now],l,mid,p1)+sum[rs[now]];
	else ans += ask(rs[now],mid+1,r,p1);
	return ans;
}

int main()
{
	scanf("%d",&n);
	rep(i,1,n) scanf("%d",&a[i]);
	build(root[0],1,n);
	rep(i,1,n){
		if(pos[a[i]]){
			update(root[i-1],root[i],1,n,pos[a[i]],-1);
			update(root[i-1],root[i],1,n,i,1);
			pos[a[i]] = i;
		}
		else update(root[i-1],root[i],1,n,i,1), pos[a[i]] = i;
	}
	scanf("%d",&q);
	rep(i,1,q){
		int xx,yy; scanf("%d%d",&xx,&yy);
		printf("%d\n",ask(root[yy],1,n,xx));
	}
	return 0;
}
 类似资料: