HDU-2665 Kth number (主席树 不带修改区间第k大)

Kth number

Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 13269    Accepted Submission(s): 4028


Problem Description
Give you a sequence and ask you the kth big number of a inteval.
 

Input
The first line is the number of the test cases. 
For each test case, the first line contain two integer n and m (n, m <= 100000), indicates the number of integers in the sequence and the number of the quaere. 
The second line contains n integers, describe the sequence. 
Each of following m lines contains three integers s, t, k. 
[s, t] indicates the interval and k indicates the kth big number in interval [s, t]
 

Output
For each test case, output m lines. Each line contains the kth big number.
 

Sample Input
 
   
1 10 1 1 4 2 3 5 6 7 8 9 0 1 3 2
 

Sample Output
 
   
2


#include   
using namespace std;    
const int maxn = 100010;  
const int INF = 1 << 30;
int a[maxn], b[maxn], rt[maxn], tot;
struct tree{
	int sum, lson, rson;
}c[maxn << 5];
int newNode(int sum, int lson, int rson){
	int id = ++tot;
	c[id].sum = sum;
	c[id].lson = lson;
	c[id].rson = rson;
	return id;
}
void insert(int& rt, int pre, int pos, int l, int r){
	rt = newNode(c[pre].sum + 1, c[pre].lson, c[pre].rson);
	if(l == r) return;
	int mid = l + r >> 1;
	if(pos <= mid) insert(c[rt].lson, c[pre].lson, pos, l, mid);
	else insert(c[rt].rson, c[pre].rson, pos, mid + 1, r);
}
int query(int Lrt, int Rrt, int l, int r, int k){
	if(l == r) return l;
	int mid = l + r >> 1;
	int sum = c[c[Rrt].lson].sum - c[c[Lrt].lson].sum;
	if(k <= sum) return query(c[Lrt].lson, c[Rrt].lson, l, mid, k);
	else return query(c[Lrt].rson, c[Rrt].rson, mid + 1, r, k - sum);
}
int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		int n, m, l, r, k, pos, num;
		tot = rt[0] = 0;
		scanf("%d %d", &n, &m);
		for(int i = 1; i <= n; ++i){
			scanf("%d", &a[i]);
			b[i] = a[i];
		}
		sort(b + 1, b + 1 + n);
		num = unique(b + 1, b + 1 + n) - b - 1;
		for(int i = 1; i <= n; ++i){
			pos = lower_bound(b + 1, b + num + 1, a[i]) - b;
			insert(rt[i], rt[i - 1], pos, 1, num);
		}
		while(m--){
			scanf("%d %d %d", &l, &r, &k);
			pos = query(rt[l - 1], rt[r], 1, num, k);
			printf("%d\n", b[pos]);
		}
	}
}    
  
/*
题意:
1e5个数,1e5次询问,每次询问区间第k大。

思路:
将1e5个数离散化后映射到值域线段树上,这样线段树可以类似于平衡树,对于单次询问
我们可以通过左右儿子的sz大小来找第k大的标号。对于多次询问我们需要用到主席树,记录
历史的某个时刻线段树上的sz。
我们按照数字出现的顺序建n棵线段树,对于没有修改到的结点,我们直接连到上一棵的结点,
对于修改到的,我们开辟新结点。这样对于区间[L,R],我们只需要看第L-1棵和第R棵线段树上
的sz来求第k大的标号即可。第L-1棵记录的是L之前出现的数字,减掉后就是在[L,R]之间出现的。
*/


你可能感兴趣的:(数据结构-主席树)