定义: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