劃分樹演算法 模板
劃分樹是一種基於線段樹的資料結構。主要用於快速求出(在log(n)的時間複雜度內)序列區間的第k大值 。
劃分樹和歸併樹都是用線段樹作為輔助的,原理是基於快排 和歸併排序 的。
劃分樹的建樹過程基本就是模擬快排過程,取一個已經排過序的區間中值,然後把小於中值的點放左邊,大於的放右邊。並且記錄d層第i個數之前(包括i)小於中值的放在左邊的數。具體看下面程式碼註釋。
模板
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define 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);
//同理 把不在區間內的 分到右子樹的元素數目排除,確定範圍
}
}
int main()
{
int m, n;
int a, b, k;
int i;
while (scanf("%d%d", &m, &n) == 2) {
for (i = 1; i <= m; ++i) {
scanf("%d", &sorted[i]);
tree[0][i] = sorted[i];
}
sort(sorted + 1, sorted + 1 + m);
build_tree(1, m, 0);
for (i = 0; i < n; ++i) {
scanf("%d%d%d", &a, &b, &k);
printf("%d\n", query(a, b, k, 1, m, 0));
}
}
return 0;
}
相關推薦
劃分樹演算法 模板
劃分樹是一種基於線段樹的資料結構。主要用於快速求出(在log(n)的時間複雜度內)序列區間的第k大值 。 劃分樹和歸併樹都是用線段樹作為輔助的,原理是基於快排 和歸併排序 的。 劃分樹的建樹過程基本就是模擬快排過程,取一個已經排過序的區間中值,然後把小於
劃分樹(模板)poj2104
Language: K-th Number Time Limit:20000MS Memory Limit:65536K Total Submissions:37013 Accepted:11911 Case Time Limit:2000MS Description You are wor
POJ2104 K-th Number —— 劃分樹(模板題)
K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 59744 Accepted: 20847 Case Time Limit: 2000MS Descriptio
劃分樹(模板+poj2104)
進入正題: 有這樣一類題目,求的是區間內的第k大數。 劃分樹的定義就是對整體的區間進行劃分,把相對於原來序列中較小的值放在左子樹,較大的放在右子樹,最後按照它的性質進行查詢以此找到要查詢的區間裡的第k大數。 例圖(圖是偷的~~~) 1.建樹 建樹是一個不停遞
NOIP複賽複習(八)STL演算法與樹結構模板
STL演算法 STL 演算法是一些模板函式,提供了相當多的有用演算法和操作,從簡單如for_each(遍歷)到複雜如stable_sort(穩定排序),標頭檔案是:#include <algorithm>。常用STL 演算法庫包括:sort快速排序演算法、二分
演算法模板(五)樹基礎演算法
最小生成樹 #include<bits/stdc++.h> #define maxn 5000 #define maxm 10000 using namespace std; inline char get(){ static char buf[30],*p1=buf,*p2=buf;
演算法模板(七) 線段樹
線段樹單點操作 #include<bits/stdc++.h> using namespace std; int a[maxn],sumv[maxn*4]; void pushup(int id){ sumv[id]=sumv[id<<1]+sumv[id<<1
【演算法模板】二叉樹的三種遍歷方式,以及根據兩種遍歷方式建樹
前言:今年九月份的PAT考試就栽在這“兩種遍歷建樹”上了,剛好沒看,剛好考到。作為自己的遺憾,今日碼完,貼在這裡留個紀念,希望能給自己警醒與警鐘。 簡要概括: 1、二叉樹的三種遍歷方式分別是 先序(先根)遍歷PreOrder,中序(中根)遍歷InOrder,後序(後根
劃分樹詳解+劃分樹模板程式碼
轉自博主語海與冰 看了一些部落格,感覺有些部落格對建樹寫的挺好,但是對於查詢區間卻一筆帶過。在看懂了之後決定自己寫一篇,加深自己的理解,也希望對正在學習劃分樹的人能夠有所幫助。 如有錯誤,敬請大佬指出。 進入正題: 有這樣一類題目,求的是區間內的第k大數。 劃分樹的定
hdu 2665 Kth number(劃分樹模板題)
題意:給定一個長度為n的序列,進行m次查詢,求出區間[l,r]中的第k大值。 思路:劃分樹模板題。上學的時候做過這道題,當時看了下劃分樹的講解,看得很頭大,然後就一直放著了。十一回家的時候在高鐵上沒什麼事情,就重新學習了一遍劃分樹。 劃分樹是通過模擬快速排序,記錄快速排序的
POJ 2104 劃分樹模板題
給出n,m n個數字 m次詢問,每次詢問(l,r)區間的第k小的數 劃分樹模板 mark一下 #include "stdio.h" #include "string.h" #include "algorithm" using namespace std; int a[
二叉搜索樹(模板)
int ret class get name cnblogs clu space tin #include<cstdio> using namespace std; const int M=9999; struct tr{ int l,r,x,size,nu
hdu 2665 Kth number(劃分樹)
first con build 這一 cst second class stream tom Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth
可持久化線段樹(主席樹)模板
spa std nod d+ sin 整理 ostream pan int 比賽時候寫的,這裏整理到這裏 #include <iostream> #include <cstdio> #include <cstring> using
POJ 2104 K-th Number(區間第k大數)(平方切割,歸並樹,劃分樹)
ac代碼 deb rank turn tracking line 查看 div 能夠 題目鏈接: http://poj.org/problem?id=2104 解題思路: 由於查詢的個數m非常大。樸素的求法無法在規定時間內求解。因此應該選用合理的方式維護數據來做到高效
BZOJ 2006 NOI2010 超級鋼琴 劃分樹+堆
heap uil 長度 tdi stream int name -1 找到 題目大意:給定一個序列,找到k個長度在[l,r]之間的序列,使得和最大 暴力O(n^2logn)。肯定過不去 看到這題的第一眼我OTZ了一下午。。。後來研究了非常久別人的題
二叉樹的模板 先序建立 二叉樹的遍歷 深度
先序遍歷 個數 iostream 葉子節點個數 pop level else 二叉樹的層次遍歷 num 關於二叉樹,基本操作都是在遞歸的基礎上完成的,二叉樹的層次遍歷是隊列實現。具體解釋看代碼 #include<iostream> #include<st
平衡樹Treap模板與原理
優化 排名 print 比較 pla ans for 後繼 每一個 這次我們來講一講Treap(splay以後再更) 平衡樹是一種排序二叉樹(或二叉搜索樹),所以排序二叉樹可以迅速地判斷兩個值的大小,當然操作肯定不止那麽多(不然我們還學什麽)。 而平衡樹在排序二叉樹的基礎上
HDU 4417 劃分樹寫法
des eve else eight url ted nta namespace pan Problem Description Mario is world-famous plumber. His “burly” figure and amazing jumping ab
hdu 3473 (劃分樹)2
mage def typedef sin else BE IT ext cati Minimum Sum Time Limit: 16000/8000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)