K-th Number (劃分樹學習)
剛開始的樸素想法,也就是老師上課說的部分快排。
但做了以後發現並沒那麼簡單,首先是qsort中,以前寫的都有等號,但不確定到底是哪個位置
於是改為無等號。
笨辦法,部分需排的由a放入b
#include <stdio.h> int a[200010]; int b[100000]; int knumber; void qqsort(int low,int high) { int l=low,h=high; b[0]=b[l]; while(l<h) { while( l<h && b[h]>=b[0] )h--; b[l]=b[h]; while( l<h && b[l]<=b[0] )l++; b[h]=b[l]; } b[l]=b[0]; if(l==knumber) return ; else if(l>knumber) qqsort(low ,h-1); else qqsort(l+1,high); } int main() { int i,j,n,m,num; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&a[i]); while(m--) { scanf("%d%d%d",&i,&j,&knumber); for(num=1;num+i-1<=j;num++) b[num]=a[i+num-1]; qqsort(1,num-1); printf("%d\n",b[knumber]); } return 0; }
因此需要劃分樹。
參考:
http://yueyue1105.blog.163.com/blog/static/431117682010716111425892/
http://www.cnblogs.com/pony1993/archive/2012/07/17/2594544.html
劃分樹是一種基於線段樹的資料結構。主要用於快速求出(在log(n)的時間複雜度內)序列區間的第k大值 。
劃分樹和歸併樹都是用線段樹作為輔助的,原理是基於快排 和歸併排序 的。
劃分樹的建樹過程基本就是模擬快排過程,取一個已經排過序的區間中值,然後把小於中值的點放左邊,大於的放右邊。並且記錄d層第i個數之前(包括i)小於中值的放在左邊的數。假設要在區間[l,r]中查詢第k大元素,t為當前節點,lch,rch為左右孩子,left,mid為節點t左邊界和中間點。
1、sum[r]-sum[l-1]>=k,查詢lch[t],區間對應為[ left+sum[l-1] , left+sum[r]-1 ]
2、sum[r]-sum[l-1]<k,查詢rch[t],區間對應為[ mid+1+l-left-sum[l-1] , mid+1+r-left-sum[r] ]
模版:
/* HDU 2665 Kth number 劃分樹 */ #include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; const int MAXN=100010; int tree[30][MAXN];//表示每層每個位置的值 int sorted[MAXN];//已經排序的數 int toleft[30][MAXN];//toleft[p][i]表示第i層從1到i有多少個數分入左邊 void build(int l,int r,int dep) { if(l==r)return; int mid=(l+r)>>1; int same=mid-l+1;//表示等於中間值而且被分入左邊的個數 for(int i=l;i<=r;i++) if(tree[dep][i]<sorted[mid])//先假設左邊的(mid - l + 1)個數都等於as[mid],然後把實際上小於as[mid]的減去 same--;//為了保持樹的平衡,不然若是很多相同的則會全部偏向一邊 int lpos=l; int rpos=mid+1; for(int i=l;i<=r;i++) { if(tree[dep][i]<sorted[mid])//比中間的數小,分入左邊 tree[dep+1][lpos++]=tree[dep][i]; else if(tree[dep][i]==sorted[mid]&&same>0) { tree[dep+1][lpos++]=tree[dep][i]; same--; } else //比中間值大分入右邊 tree[dep+1][rpos++]=tree[dep][i]; toleft[dep][i]=toleft[dep][l-1]+lpos-l;//從1到i放左邊的個數 } build(l,mid,dep+1); build(mid+1,r,dep+1); } //查詢區間第k大的數,[L,R]是大區間,[l,r]是要查詢的小區間 //假設要在區間[l,r]中查詢第k大元素,t為當前節點,lch,rch為左右孩子,left,mid為節點t左邊界和中間點 //sum[r]-sum[l-1]>=k,查詢lch[t],區間對應為[ left+sum[l-1], left+sum[r]-1 ] //sum[r]-sum[l-1]<k,查詢rch[t],區間對應為[ mid+1+l-left-sum[l-1], mid+1+r-left-sum[r] ] int query(int L,int R,int l,int r,int dep,int k) { if(l==r)return tree[dep][l]; int mid=(L+R)>>1; int cnt=toleft[dep][r]-toleft[dep][l-1];//[l,r]中位於左邊的個數 if(cnt>=k) { //L+要查詢的區間前被放在左邊的個數 int newl=L+toleft[dep][l-1]-toleft[dep][L-1]; //左端點加上查詢區間會被放在左邊的個數 int newr=newl+cnt-1; return query(L,mid,newl,newr,dep+1,k); } else { int newr=r+toleft[dep][R]-toleft[dep][r]; int newl=newr-(r-l-cnt); return query(mid+1,R,newl,newr,dep+1,k-cnt); } } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; int n,m; int s,t,k; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); memset(tree,0,sizeof(tree));//這個必須 for(int i=1;i<=n;i++)//從1開始 { scanf("%d",&tree[0][i]); sorted[i]=tree[0][i]; } sort(sorted+1,sorted+n+1); build(1,n,0); while(m--) { scanf("%d%d%d",&s,&t,&k); printf("%d\n",query(1,n,s,t,0,k)); } } return 0; }
相關推薦
K-th Number (劃分樹學習)
剛開始的樸素想法,也就是老師上課說的部分快排。 但做了以後發現並沒那麼簡單,首先是qsort中,以前寫的都有等號,但不確定到底是哪個位置 於是改為無等號。 笨辦法,部分需排的由a放入b #include <stdio.h> int a[200010]; int
POJ2104 K-th Number —— 劃分樹(模板題)
K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 59744 Accepted: 20847 Case Time Limit: 2000MS Descriptio
POJ 2104 K-th Number (主席樹)
std +++ esp space ctype == string uniq upd 題意:給定一個序列,然後有 q 個詢問,每次詢問 l - r 區間內的第 k 大的值。 析:很明顯的主席樹,而且還是裸的主席樹,先進行離散化,然後用主席樹進行查詢就好。 代碼如下: #p
Poj 2104 K-th Number 主席樹模版題
OS tdi pda sig signed begin ostream air color 題意:離線詢問[l,r]區間第k大 題解:模版題,入門題 #include <iostream> #include <cstdio> #inclu
【poj2104】K-th Number 歸併樹
說起來這是第二次A這個題了23333 第一次是分塊,戳這裡 歸併樹比分塊打起來爽,速度還快,簡直好評 思想:線段樹套陣列【霧。線段樹每個節點儲存它所表示的區間的有序數字排列。實現就是在歸併排序的過程中儲存歸併排序的過程。蠻好打的。 並且除錯什麼的也沒出什
POJ 2104 K-th Number 主席樹模板題
題意:給定一個長度為n的無序陣列,然後有m組詢問l r k,求區間[l,r]中的第k大數 思路:以前用劃分樹做過,現在學習主席樹,模板題。個人對主席樹的理解:就是把線段樹更新過程中所有的歷史狀態記錄下來,例如更新m次的話,那麼就會有m種狀態,直接建m棵線段樹的話,時間和空
POJ 2104 K-th Number [主席樹入門]【資料結構】
題目連結:http://poj.org/problem?id=2104 ———————————————————————————————————————————————— K-th Number Time Limit: 20000MS Memory
POJ 2104 K-th Number 主席樹(求區間第k大)
主席書資料 題意:給出n個數,m次詢問,[x,y]內第k小的數時多少?n<=1e5,m<=5000 主席樹:對原序列的每個字首i都建立一個線段樹 維護值域[l,r]中的每個數,在字首i的
poj 2104 K-th Number 主席樹+超級詳細解釋
題目大意:給出一段數列,讓你求[L,R]區間內第幾大的數字! 在這裡先介紹一下主席樹! 如果想了解什麼是主席樹,就先要知道線段樹,主席樹就是n棵線段樹,因為線段樹只能維護最大值或者最小值,要想求出第二大的數字怎麼辦呢?兩顆線段樹唄!好,那麼第n大呢,就可
POJ 2104 K-th Number(區間第k大數)(平方切割,歸並樹,劃分樹)
ac代碼 deb rank turn tracking line 查看 div 能夠 題目鏈接: http://poj.org/problem?id=2104 解題思路: 由於查詢的個數m非常大。樸素的求法無法在規定時間內求解。因此應該選用合理的方式維護數據來做到高效
K-th Number POJ - 2104 (主席樹 學習詳解)
https://cn.vjudge.net/problem/POJ-2104 題意 給你N個數 嗎、M次查詢,每次查詢給你 IJK 問第I個數到第J個數中第K大 思路 字典樹,每新增一個數都建立一棵線段樹,J和I 做減法就可以的到這個區間的線段樹 #include <c
【劃分樹】K-th Number
/* 此題採用劃分歸併樹進行計算 * 題號:poj 2104 */ #include #include #include #include #include #define MAXN 100010 using namespace std; int num[MAXN]; // 用於儲存輸入的陣列
POJ 2104 K-th Number (劃分樹 / 主席樹)
Description You are working for Macrohard company in data structures department. After failing yo
POJ 2104 K-th Number (劃分樹,主席樹寫過了,這次是整體二分解法 )
還是先描述一下題意: 給出一個長度為n的數列,m次詢問區間內的第k大數 對劃分樹,主席樹和整體二分通過這題做了一下比較 劃分樹 1000ms+ 主席樹 2000ms+ 整體二分 1500ms+ 整體二分介於兩者之前,對於這題複雜度約莫是O( (n+m)log(n+m)l
POJ 2104 K-th Number(主席樹)
ber sca first n) 次數 example == scan sorted K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 5742
【POJ 2104】【主席樹模板題】K-th Number
題意: 靜態詢問區間第K大問題。給出一個數組,然後多次詢問某一區間第K大數是多少。 思路: 典型的主席樹模板題。 所以就大致講一下靜態主席樹
A - K-th Number POJ - 2104 -主席樹第一彈-第K大的數
感謝:http://www.cnblogs.com/zyf0163/p/4749042.html https://blog.csdn.net/qq_24451605/article/details/49031123 裸題多次查詢給定區間L—R內第K大的數 #include&
HDOJ2665 Kth number --- 劃分樹求區間第k小數
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 c
歸併樹(POJ 2104 K-th Number)
在求解區間第k個數的問題,除了劃分樹以外我們還可以使用另一種高效的方法 ------ 歸併樹。 1、演算法描述 所謂歸併樹,就是利用線段樹的建樹過程,將歸併排序的過程儲存。(不會線段樹:here,不會歸併排序:here)。在說明歸併樹之前我們
poj 2104 K-th Number (主席樹入門模板題)
摘抄了一段主席樹的解釋:所謂主席樹呢,就是對原來的數列[1..n]的每一個字首[1..i](1≤i≤n)建立一棵線段樹,線段樹的每一個節點存某個字首[1..i]中屬於區間[L..R]的數一共有多少個(比如根節點是[1..n],一共i個數,sum[root]