定義:html
權值線段樹,基於普通線段樹,可是不一樣。ios
舉個栗子:對於一個給定的數組,普通線段樹能夠維護某個子數組中數的和,而權值線段樹能夠維護某個區間內數組元素出現的次數。數組
在實現上,因爲值域範圍一般較大,權值線段樹會採用離散化或動態開點的策略優化空間。單次操做時間複雜度o(logn) ide
權值線段樹的節點用來表示一個區間的數出現的次數 例如: 數 1和2 分別出現3次和5次,則節點1記錄 3,節點2 記錄5, 1和2的父節點記錄它們的和8 .優化
存儲結構:spa
堆式存儲:rt ,l, r, rt<<! , l, m rt<<1|1 ,m+1, r .net
結點式存儲 struct Node { int sum ,l , r :};code
基本做用:htm
查詢第k小或第k大。blog
查詢某個數排名。
查詢整幹數組的排序。
查詢前驅和後繼(比某個數小的最大值,比某個數大的最小值)
基本操做:
單點修改 (單個數出現的次數+1)
void update(int l,int r,int rt,int pos) // 當前區間範圍 l r 節點 rt 位置 pos { if(l==r) t[rt]++; else { int mid=(l+r)/2; if(pos<=mid) add(l,mid,rt*2,pos); else add(mid+1,r,rt*2+1,pos); t[rt]=t[rt*2]+t[rt*2+1]; { }
查詢一個數出現的次數
int query(int l,int r,int rt,int pos) { if(l==r) return t[rt]; else { int mid=(l+r)/2; if(pos<=mid) return find(l,mid,rt*2,pos); else return find(mid+1,r,rt*2+1,pos); } }
查詢一段區間數出現的次數 查詢區間 【x,y]
遞歸+二分
int query(int l,int r,int rt,int x,int y) { if(l==x&&r==y) return t[rt]; else { int mid=(l+r)/2; if(y<=mid) return find(l,mid,rt*2,x,y); else if(x>mid) return find(mid+1,r,rt*2+1,x,y); else return find(l,mid,rt*2,x,mid)+find(mid+1,r,rt*2+1,mid+1,y); } }
查詢全部數的第k大值
這是權值線段樹的核心,思想以下:
到每一個節點時,若是右子樹的總和大於等於k kk,說明第k kk大值出如今右子樹中,則遞歸進右子樹;不然說明此時的第k kk大值在左子樹中,則遞歸進左子樹,注意:此時要將k kk的值減去右子樹的總和。
爲何要減去?
若是咱們要找的是第7 77大值,右子樹總和爲4 44,7−4=3 7-4=37−4=3,說明在該節點的第7 77大值在左子樹中是第3 33大值。
最後一直遞歸到只有一個數時,那個數就是答案。
int kth(int l,int r,int rt,int k) { if(l==r) return l; else { int mid=(l+r)/2,s1=f[rt*2],s2=f[rt*2+1]; if(k<=s2) return kth(mid+1,r,rt*2+1,k); else return kth(l,mid,rt*2,k-s2); } }
模板題:
給你一個序列,你能夠循環左移,問最小的逆序對是多少???
逆序對實際上是尋找比這個數小的數字有多少個,這個問題其實正是權值線段樹所要解決的
咱們把權值線段樹的單點做爲1-N的數中每一個數出現的次數,並維護區間和,而後從1-N的數,在每一個位置,查詢比這個數小的數字的個數,這就是當前位置的逆序對,而後把當前位置數的出現的次數+1,就能獲得答案。
而後咱們考慮循環右移。咱們每次循環右移,至關於把序列最左邊的數字給放到最右邊,而位於序列最左邊的數字,它對答案的功效僅僅是這個數字大小a[i]-1,由於比這個數字小的數字所有都在它的後面,而且這個數字放到最後了,它對答案的貢獻是N-a[i],由於比這個數字大數字所有都在這個數字的前面,因此每當左移一位,對答案的貢獻其實就是
Ans=Ans-(a[i]-1)+n-a[i]
因爲數字從0開始,咱們建樹從1開始,咱們把全部數字+1便可
#include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> using namespace std; const int maxx = 5005; int tree[maxx<<2]; inline int L(int root){return root<<1;}; inline int R(int root){return root<<1|1;}; inline int MID(int l,int r){return (l+r)>>1;}; int a[maxx]; void update(int root,int l,int r,int pos){ if (l==r){ tree[root]++; return; } int mid=MID(l,r); if (pos<=mid){ update(L(root),l,mid,pos); }else { update(R(root),mid+1,r,pos); } tree[root]=tree[L(root)]+tree[R(root)]; } int query(int root,int l,int r,int ql,int qr){ if (ql<=l && r<=qr){ return tree[root]; } int mid=MID(l,r); if (qr<=mid){ return query(L(root),l,mid,ql,qr); }else if (ql>mid){ return query(R(root),mid+1,r,ql,qr); }else { return query(L(root),l,mid,ql,qr)+query(R(root),mid+1,r,ql,qr); } } int main(){ int n; while(~scanf("%d",&n)){ int ans=0; memset(tree,0,sizeof(tree)); for (int i=1;i<=n;i++){ scanf("%d",&a[i]); a[i]++; ans+=query(1,1,n,a[i],n); update(1,1,n,a[i]); } int minn=ans; for (int i=1;i<=n;i++){ ans=ans+(n-a[i]+1)-a[i]; minn=min(ans,minn); } printf("%d\n",minn); } return 0; }
進階知識 主席樹:http://www.javashuo.com/article/p-wqmknicq-bc.html
參考博客:https://blog.csdn.net/qq_39565901/article/details/81782611