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 <bits/stdc++.h>  
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]之間出現的。
*/


發佈了165 篇原創文章 · 獲贊 4 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章