區間第k大的數 主席樹

2021-08-03 15:54:37 字數 2410 閱讀 2036

套主席樹求區間第k小的數的模板,然後求區間[l,r]第k大的數就等於求區間[l,r]第r-l+1-k小的數(下標從1開始)

區間第k小值問題

有n個數,多次詢問乙個區間[l,r]中第k小的值是多少。

查詢區間[l,r]中的第k小值

我們按照從1到n的順序依次將資料插入可持久化的線段樹中,將會得到n+1個版本的線段樹(包括初始化的版本),將其編號為0~n。

可以發現所有版本的線段樹都擁有相同的結構,它們同乙個位置上的結點的含義都相同。

考慮第i個版本的線段樹的結點p,p中儲存的值表示[1,i]這個區間中,p結點的值域中所含的元素個數;

假設我們知道了[1,r]區間中p結點的值域中所含的元素個數,也知道[1,l-1]區間中p結點的值域中所包含的元素個數,顯然用第乙個個數減去第二個個數,就可以得到[l,r]區間中的元素個數。

因此我們對於乙個查詢[l,r],同步考慮兩個根root[l-1]與root[r],用它們同乙個位置的結點的差值就表示了區間[l,r]中的元素個數,利用這個性質,從兩個根節點,向左右兒子中遞迴的查詢第k小數即可。

常數優化的技巧

一種在常數上減小記憶體消耗的方法:

插入值時候先不要一次新建到底,能留住就留住,等到需要訪問子節點時候再建下去。

這樣理論記憶體複雜度依然是o(nlg^2n),但因為實際上很多結點在查詢時候根本沒用到,所以記憶體能少用一些

#include 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define fin freopen("input.txt","r",stdin);

#define fout freopen("output.txt","w+",stdout);

using

namespace

std;

typedef

long

long ll;

const

int inf = 0x3f3f3f3f;

const

int mod = 1e9 + 7;

const

double eps=1e-8;

const

double pi=acos(-1.0);

const

int maxn=50002;

struct node

tr[maxn*20];

int cur,root[maxn];

inline

void push_up(int o)

int build(int l,int r)

int mid=(l+r)>>1;

tr[k].ls=build(l,mid);

tr[k].rs=build(mid+1,r);

push_up(k);

return k;

}int update(int o,int l,int r,int pos,int val)

int mid=(l+r)>>1;

if(pos<=mid)

tr[k].ls=update(tr[o].ls,l,mid,pos,val);

else

tr[k].rs=update(tr[o].rs,mid+1,r,pos,val);

push_up(k);

return k;

}int query(int l,int r,int o,int v,int kth)

int num[maxn];

int sortnum[maxn];

int main()

sort(sortnum+1,sortnum+1+n);

int cnt=1;

for(int i=2;i<=n;i++)

root[0]=build(1,cnt);

for(int i=1;i<=n;i++)

int q;

scanf("%d",&q);

for(int i=0;iint l,r,k;

scanf("%d %d %d",&l,&r,&k);

int idx=query(1,cnt,root[l],root[r+1],r-l+1-k+1);//模板的下標從1開始,則求的是root[l-1]與root[r]的值

printf("%d\n",sortnum[idx]);}}

}

區間第k大(主席樹)

學了一下主席樹模板題,當初看了網上的主席樹講解都沒有看懂,後面看了嗶哩嗶哩的uestc的主席樹,終於看懂了思想。每次更新的複雜度都為logn。每次更新的話就是對要更新的點路徑上的點重新更加乙個,然後進行對沒有影響的那些進行連邊。然後用乙個root記錄每乙個線段樹的根節點下標。include incl...

主席樹區間第K大

主席樹的實質其實還是一顆線段樹,然後每一次修改都通過上一次的線段樹,來新增新邊,使得每次改變就改變logn個節點,很多節點重複利用,達到節省空間的目的。1.不帶修改的區間第k大。hdu 2665 模板題 1 include2 using namespace std 3 define fopen fr...

主席樹求區間第k大

主席樹是可持久化線段樹,維護 權值個數 線段樹的字首和。相當於對每個區間 1,i 建立n顆線段樹。我們用乙個區間內的數的出現個數建線段樹,所以資料大小較大時一般進行離散化。建的是權值線段樹,即用數值作為區間,每個節點存該數出現的次數,所以query返回的其實是離散後的陣列b的下標idx,最終結果為b...