一.權值線段樹與線段樹的區別:
--權值線段樹維護數的個數,數組下標代表整個值域(如果值域太大,可以離散化,后面會有介紹)
--線段樹則是直接維護每個數
二.權值線段樹的用處
1.尋找第K大(整個區間,即左邊界為1,右邊界為n)
2.逆序對(呵呵歸並也能求)
3.最大差&最小差(??!)
4..............
三.權值線段樹的具體實現
沒什么好說的,直接上代碼(丑):
建樹(build):
inline void build(int root,int L,int R) { tree[root].l=L; tree[root].r=R; if(L==R)
{
//初始化
return;
}
int mid=(L+R)>>1; build(root<<1,L,mid);//建左兒子 build(root<<1|1,mid+1,R);//建右兒子 }
更新(update):
inline void update(int root,int t) { if(tree[root].l==tree[root].r) { //更新數據
return; } int mid=(tree[root].l+tree[root].r)>>1; if(t<=mid) update(root<<1,t);//在左兒子中 else update(root<<1|1,t);//在右兒子中 //維護一下(push_up) }
這兩個部分與普通線段樹沒什么兩樣啊----------
詢問整體第k大(query):
在線段樹上進行二分:
先看左子樹數的個數,設其個數為f.
如果f>=t遞歸進入左子樹尋找
如果f<k遞歸進入右子樹尋找第f-k大
即整體二分
//詢問整個區間第t大(這里t代表k)
//tree[root].s代表tree[root].l至tree[root].r值域中數的個數總和
inline int query(int root,int t) { if(tree[root].l==tree[root].r) return tree[root].l;//由於數組下標維護的是值域,直接返回其下標 if(t<=tree[root<<1].s) return query(root<<1,t);//在左子樹中 else return query(root<<1|1,t-tree[root<<1].s);//在右子樹中,記得減去左子樹個數
}
四.例題
黑匣子https://www.luogu.org/problemnew/show/P1801
仔細模擬即可。
直接上AC代碼:
#include<bits/stdc++.h> #define N 200005 using namespace std; int m,n,k; int a[N],b[N],u[N]; struct MM{ int l,r,s; }tree[N<<2]; inline void build(int root,int L,int R) { tree[root].l=L; tree[root].r=R; if(L==R) return; int mid=(L+R)>>1; build(root<<1,L,mid); build(root<<1|1,mid+1,R); } inline void update(int root,int t) { if(tree[root].l==tree[root].r) { tree[root].s++;//個數加一 return; } int mid=(tree[root].l+tree[root].r)>>1; if(t<=mid) update(root<<1,t); else update(root<<1|1,t); tree[root].s=tree[root<<1].s+tree[root<<1|1].s; } inline int query(int root,int t) { if(tree[root].l==tree[root].r) return tree[root].l; if(t<=tree[root<<1].s) return query(root<<1,t); else return query(root<<1|1,t-tree[root<<1].s); } int main() { cin>>m>>n; for(int i=1;i<=m;i++) { cin>>a[i]; b[i]=a[i]; } for(int i=1;i<=n;i++) cin>>u[i]; sort(b+1,b+m+1); int s=unique(b+1,b+m+1)-(b+1);//離散化(若值域很大),s是數組b中不重復的數的個數 build(1,1,s);//依s建樹 int h=0; while(n!=h) { h++; for(int i=u[h-1]+1;i<=u[h];i++) { int v=lower_bound(b+1,b+s+1,a[i])-b;//v是a[i]在數組b中所處的位置(注意之前數組b排了序) update(1,v); } cout<<b[query(1,++k)]<<endl; } return 0; }
蒟蒻第一次寫博客,請大佬們多多提建議