pku2104 第k大數-劃分樹做法

 http://poj.org/problem?id=2104

題意:求任意區間第k大數

 

分析:劃分樹主要參考了大牛博客

http://www.notonlysuccess.com/?p=142

http://blog.sina.com.cn/s/blog_5f5353cc0100ki2e.html

劃分樹兼容了快排和歸併的特點。。。採用劃分的思想逐漸往下劃,最後葉子節點的元素就是排好序的元素。。。其實可以理解成穩定的快排。。。和歸併樹一樣,記錄每一層的結果。。。並記錄toleft[]。。。

查找的過程,總是查詢[ tt[p].l [ l, r] tt[p].r ]。。。這裏就有了幾個區間,想想,劃分樹劃分下去的時候[tt[p].l, l-1]中不大於中位數的一定靠在最前,[l, r]中不大於中位數的排在其後。。。我們通過計算[l, r]中不大於中位數的個數l2來和k比較,來確定是到左子樹還是右子樹去找,查找的範圍都可以通過兩個區間劃分到左子樹的個數來縮小。。。。

 

時間複雜度nlgn+mlgn,比歸併樹nlgn+m(lgn)^3快多了,m越大效果越好。。

代碼:

比歸併樹快一倍吧。。。

#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;

const int N=100010;
const int DEEP=20;
int n, m, sa[N];
int seq[DEEP][N]; //歸併樹
int toleft[DEEP][N]; //記錄當前層左邊界到i位置有多少劃入左子樹
struct node
{
	int l, r, mid;
} tt[N*4];

void build(int l, int r, int d, int p)
{
	tt[p].l = l;
	tt[p].r = r;
	tt[p].mid = (l+r)>>1;
	if(l==r)
		return;

	int lsame, i, j, ii;
	lsame = tt[p].mid-tt[p].l+1;
	for(i=tt[p].l; i<=tt[p].r; i++)
		if(seq[d][i]<sa[tt[p].mid])
			lsame--;
	i = tt[p].l;
	j = tt[p].mid+1; 
	for(ii=tt[p].l; ii<=tt[p].r; ii++)
	{
		if(ii==tt[p].l)
			toleft[d][ii] = 0;
		else
			toleft[d][ii] = toleft[d][ii-1];
		if(seq[d][ii]<sa[tt[p].mid])
		{
			toleft[d][ii]++;
			seq[d+1][i++] = seq[d][ii];
		}
		else if(seq[d][ii]>sa[tt[p].mid])
		{
			seq[d+1][j++] = seq[d][ii];
		}
		else
		{
			if(lsame>0)
			{
				toleft[d][ii]++;
				lsame--;
				seq[d+1][i++] = seq[d][ii];
			}
			else
			{
				seq[d+1][j++] = seq[d][ii];
			}
		}
	}
	build(l, tt[p].mid, d+1, p*2);
	build(tt[p].mid+1, r, d+1, p*2+1);
}
int query(int l, int r, int k, int d, int p)
{
	if(l==r)
		return seq[d][l];

	//l1爲[tt[p].l, l-1]之間划進左子樹的個數,l2爲[l, r]之間划進左子樹的個數。。
	int l1, l2; 
	if(l==tt[p].l)
	{
		l1 = 0;
		l2 = toleft[d][r];
	}
	else
	{
		l1 = toleft[d][l-1];
		l2 = toleft[d][r]-toleft[d][l-1];
	}
	int ll, rr;
	if(k<=l2)
	{
		ll = tt[p].l + l1;
		rr = tt[p].l + l1+l2 -1;
		return query(ll, rr, k, d+1, p*2);
	}
	else
	{
		//r1表示[tt[p].l, l]之間划進右子樹的個數,r2爲[l, r]之間划進左子樹的個數。。
		int r1, r2;
		r1 = l-tt[p].l-l1;
		r2 = r-l+1-l2;
		ll = tt[p].mid+1 + r1;
		rr = tt[p].mid+1 + r1+r2 -1;
		return query(ll, rr, k-l2, d+1, p*2+1);
	}
}

int main()
{
	int i, j, x, y, k;
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		for(i=0; i<n; i++)
		{
			scanf("%d", &seq[0][i]);
			sa[i] = seq[0][i];
		}
		sort(sa, sa+n);
		build(0, n-1, 0, 1);
		
		while(m--)
		{
			scanf("%d%d%d", &x, &y, &k);
			printf("%d\n", query(x-1, y-1, k, 0, 1));
		}
	}

	return 0;
}


 

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