(数据结构)划分树(查询区间第 k 大)和题库

求区间第k大!也可以用主席树,过几天去搞下主席树吧!

直接上板子
/*
* 		划分树(查询区间第 k 大)
	//	toleft[p][i] 表示第 i 层从 1 到 i 有数分入左边
	// 	sorted[MAX_SIZE];		已经排好序的数据 
	// 	tree[25][MAX_SIZE];     tree[i][n]  表示的是第i层有n个数字 
*/
const int  MAX_SIZE = 100005;  
int sorted[MAX_SIZE];		
int toleft[25][MAX_SIZE];  
int tree[25][MAX_SIZE];  	
void build_tree(int left, int right, int deep)   
{  
    int i;  
    if (left == right) return ;  
    int mid = (left + right) >> 1;  
    int same = mid - left + 1; 				//位于左子树的数据  
    for (i = left; i <= right; ++i) {		//计算放于左子树中与中位数相等的数字个数  
        if (tree[deep][i] < sorted[mid]) {  
            --same;  
        }  
    }  
    int ls = left;  
    int rs = mid + 1;  
    for (i = left; i <= right; ++i) {  
        int flag = 0;  
        if ((tree[deep][i] < sorted[mid]) || (tree[deep][i] == sorted[mid] && same > 0)) {  
            flag = 1;  
            tree[deep + 1][ls++] = tree[deep][i];  
            if (tree[deep][i] == sorted[mid])  
                same--;  
        } else {  
            tree[deep + 1][rs++] = tree[deep][i];  
        }  
        toleft[deep][i] = toleft[deep][i - 1]+flag;  
    }  
    build_tree(left, mid, deep + 1);  
    build_tree(mid + 1, right, deep + 1);  
}  
int query(int left, int right, int k, int L, int R, int deep)  
{  
    if (left == right)  
        return tree[deep][left];  
    int mid = (L + R) >> 1;  
    int x = toleft[deep][left - 1] - toleft[deep][L - 1];//位于left左边的放于左子树中的数字个数  
    int y = toleft[deep][right] - toleft[deep][L - 1];//到right为止位于左子树的个数  
    int ry = right - L - y;//到right右边为止位于右子树的数字个数  
    int cnt = y - x;//[left,right]区间内放到左子树中的个数  
    int rx = left - L - x;//left左边放在右子树中的数字个数  
    if (cnt >= k) {  
        //printf("sss %d %d %d\n", xx++, x, y);  
        return query(L + x, L + y - 1, k, L, mid, deep + 1);
        // 因为x不在区间内 所以没关系 所以先除去,从L+x开始,然后确定范围
    }  
    else {  
        //printf("qqq %d %d %d\n", xx++, x, y);  
        return query(mid + rx + 1, mid + 1 + ry, k - cnt, mid + 1, R, deep + 1); 
        //同理 把不在区间内的 分到右子树的元素数目排除,确定范围 
    }  
}
void solve(){
	
	int n, m;
	int l, r, k;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++){
		scanf("%d",&sorted[i]);
		tree[0][i] = sorted[i];
	}
	sort(sorted + 1, sorted + 1 + n);
	build_tree(1, n, 0);
	for(int i = 0; i < m; i++){
		
		scanf("%d%d%d", &l, &r, &k);
		int ans = query(l, r, k, 1, n, 0);
		printf("%d\n",ans); 
	}  
}  
int main()  
{  
    int t;
	scanf("%d",&t);
	//t = 1; 
	while(t--){
		solve();
	}  
    return 0;  
}  

先来个板子题:

题目传送门POJ2104
**题意:**直接裸!静态求区间第k大
我的理解: 直接上板子

直接上板子
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<string>
#include<set>
#define sa(t) scanf("%d",&t)
#define SA(t) scanf("%lld", %t)
#define PF(t) printf("%lld",t)
#define pf(t) printf("%d", t)
#define PFF(t) printf("%lld\n",t)
#define pff(t) printf("%d\n", t)

using namespace std;


/*
* 	划分树(查询区间第 k 大)
	//toleft[p][i] 表示第 i 层从 1 到 i 有数分入左边
	// sorted[MAX_SIZE];		已经排好序的数据 
	// tree[25][MAX_SIZE];     tree[i][n]  表示的是第i层有n个数字 
*/
const int  MAX_SIZE = 100005;  
int sorted[MAX_SIZE];		
int toleft[25][MAX_SIZE];  
int tree[25][MAX_SIZE];  	
void build_tree(int left, int right, int deep)   
{  
    int i;  
    if (left == right) return ;  
    int mid = (left + right) >> 1;  
    int same = mid - left + 1; 				 
    for (i = left; i <= right; ++i) {	
        if (tree[deep][i] < sorted[mid]) {  
            --same;  
        }  
    }  
    int ls = left;  
    int rs = mid + 1;  
    for (i = left; i <= right; ++i) {  
        int flag = 0;  
        if ((tree[deep][i] < sorted[mid]) || (tree[deep][i] == sorted[mid] && same > 0)) {  
            flag = 1;  
            tree[deep + 1][ls++] = tree[deep][i];  
            if (tree[deep][i] == sorted[mid])  
                same--;  
        } else {  
            tree[deep + 1][rs++] = tree[deep][i];  
        }  
        toleft[deep][i] = toleft[deep][i - 1]+flag;  
    }  
    build_tree(left, mid, deep + 1);  
    build_tree(mid + 1, right, deep + 1);  
}  
int query(int left, int right, int k, int L, int R, int deep)  
{  
    if (left == right)  
        return tree[deep][left];  
    int mid = (L + R) >> 1;  
    int x = toleft[deep][left - 1] - toleft[deep][L - 1];
    int y = toleft[deep][right] - toleft[deep][L - 1]; 
    int ry = right - L - y;  
    int cnt = y - x;
    int rx = left - L - x;
    if (cnt >= k) {  
        
        return query(L + x, L + y - 1, k, L, mid, deep + 1);
        
    }  
    else {  
         
        return query(mid + rx + 1, mid + 1 + ry, k - cnt, mid + 1, R, deep + 1); 
       
    }  
}
void solve(){
	
	int n, m;
	int l, r, k;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++){
		scanf("%d",&sorted[i]);
		tree[0][i] = sorted[i];
	}
	sort(sorted + 1, sorted + 1 + n);
	build_tree(1, n, 0);
	for(int i = 0; i < m; i++){
		
		scanf("%d%d%d", &l, &r, &k);
		int ans = query(l, r, k, 1, n, 0);
		printf("%d\n",ans); 
	}  
}  
int main()  
{  
    int t;
	//scanf("%d",&t);
	t = 1; 
	while(t--){
		solve();
	}  
    return 0;  
}  

持续更新中!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值