hud 2665 Kth number 划分树 这道题真坑!!明明求的是区间第K小数。。。

154 篇文章 1 订阅
2 篇文章 0 订阅

Kth number

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


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
这道题的这坑!!明明求的是区间第K小的数,题意却给人一种求区间第K大的数感觉,要不是看了discuss,我可能会wrong一辈子!!
这道题就是一道划分树的模板题,不是太难,刚学划分树,拿来练手 ,是最好不过的了!
#include <cstdio>
#include <algorithm>

#define MAX 110000

using namespace std ;

int sorted[MAX] , tree[30][MAX] ,toLeft[30][MAX];

void build(int left , int right , int deep)
{
	if(left == right)
	{
		return ;
	}
	int mid = (left+right)>>1 ;
	int same = mid-left+1 ;
	for(int i = left ; i <= right ; ++i)
	{
		if(tree[deep][i]<sorted[mid])
			--same ;
	}
	
	int ls = left;
	int rs = mid + 1 ;
	for(int i = left ; i <= right ; ++i)
	{
		int flag = 0 ;
		if(tree[deep][i] < sorted[mid] || (tree[deep][i]==sorted[mid] && same) )
		{
			tree[deep+1][ls++]=tree[deep][i] ;
			if(tree[deep][i] == sorted[mid]) --same ;
			flag = 1; 
		}
		else
		{
			tree[deep+1][rs++] = tree[deep][i] ;
		}
		toLeft[deep][i] = toLeft[deep][i-1] + flag ;
	}
	build(left,mid,deep+1);
	build(mid+1,right,deep+1);
}


//x,y是小区间	,, L,R是大区间 
int query(int x , int y , int k , int L , int R , int deep)
{
	if(x == y)
	{
		return tree[deep][x] ;
	}
	int mid = (L+R)>>1 ;
	int lxl = toLeft[deep][x-1]-toLeft[deep][L-1];//L与x之间的有多少在左子树的 
	int lxr = x-L-lxl ;		//Y: L与x之间在右子树的
	int xyl = toLeft[deep][y]-toLeft[deep][x-1]	;// x与y之间有多少在左子树的 
	int lyr = y-L+1-(toLeft[deep][y]-toLeft[deep][L-1]) ;// L与y之间有多少右子树额 
	int lyl = toLeft[deep][y]-toLeft[deep][L-1] ; // L与y之间有多少左子树的 
	if(k<=xyl)
	{
		return query(L+lxl,L+lyl-1,k,L,mid,deep+1);
	}
	else
	{
		return query(mid+1+lxr , mid+lyr,k-xyl,mid+1,R,deep+1);
	}

}
int main()
{
	int t ;
	scanf("%d",&t) ;
	while(t--)
	{
		int n , m ;
		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+n+1) ;
		build(1,n,0);
		int left, right , k;
		for(int i = 0 ; i < m ; ++i)
		{
			scanf("%d%d%d",&left,&right,&k) ;
			printf("%d\n",query(left,right,k,1,n,0));//把k改成这个right-left+2-k,就是求区间第K大的数了 
		}
	} 
	return 0 ;
}

这个代码以后就作为我的模板了



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值