权值线段树学习(模板+例题)

之前写一道题的时候,看到了一个数据结构叫做权值线段树,跟普通的线段树不太一样,一直没有仔细看,上课无聊,随手推了推,画了几张图,感觉容易多了。

权值线段树

普通线段树基本上会点数据结构的人都知道了,维护区间的一些属性(最值,区间和什么的)。权值线段树虽然挂了一个权值的名字,其实本职工作仍然离不开对区间的维护,只不过他维护的是 “权值”。

权值线段树作用

权值线段树的作用就是查找一个区间的第k大的值。

原理

一开始我还有点迷,怎么样才能找到区间内第k大的值呢?自己还画了半天(妄想自己推出权值线段树,石乐志)。

后来看看别人的原理(说的都是什么啊,要么高深的很,要么直接就是代码。。)结合自己的脑补,搞出了个个人认为就是权值线段树的东西(目前为止仍是个人认为的,有大佬觉得不对的话,请务必留言指出,十分感谢!)

下面就开始口胡了:

其实很简单,我们每个节点的位置表示的是元素的值,然后节点中的元素表示的是元素的数量。如图:

大概就是这样了。是不是感觉有点头绪了?

接下来修改一下区间:例如,向数组中插入数字3和5

好了,现在线段树修改成这个样子了。看到这,是不是对权值线段树有一个基本的了解了。

那么查询3~7区间中第二大的数:就是3了。

步骤就是蓝色的步骤了:

好了,这个就是基本的思路了(口胡完毕),现在知道思路后,实现很显然就好实现了。

代码实现就不专门写了,还是线段树的那个板子。

然后就是试试这个算法了,找一些OJ测试一下

逆序数:

这玩意还能用来求逆序数!!牛逼牛逼!到现在我倒觉得跟树状数组差不多了23333.

HDU-1394-Minimum Inversion Number(求最小逆序数)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394

题目大意:如题

思路:很明显,人尽皆知**题。

ACCode:

// luogu-judger-enable-o2
//#pragma comment(linker, "/STACK:1024000000,1024000000")
 
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<time.h>
 
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define ll long long
#define Pair pair<ll,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// 水印
//std::ios::sync_with_stdio(false);
//  register
const int MAXN=1e4+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=192600817;
const double EPS=1.0e-8;
const double PI=acos(-1.0);

int Tree[MAXN<<2];
int a[MAXN];
int n;

void Update(int l,int r,int x,int rt){//修改一个点 
	if(l==r){
		Tree[rt]++;
		return ;
	}int mid=(l+r)>>1;
	if(x<=mid) Update(l,mid,x,rt<<1);
	else Update(mid+1,r,x,rt<<1|1);
	Tree[rt]=Tree[rt<<1]+Tree[rt<<1|1];
}
int Query(int ql,int qr,int l,int r,int rt){
	if(l>=ql&&r<=qr){
		return Tree[rt];
	}int mid=(l+r)>>1;
	int ans=0;
	if(ql<=mid) ans+=Query(ql,qr,l,mid,rt<<1);
	if(qr>mid) ans+=Query(ql,qr,mid+1,r,rt<<1|1);
	return ans;
}
int main(){
	while(~scanf("%d",&n)){
		clean(Tree,0);
		int x,ans=0;
		for(int i=1;i<=n;++i){
			scanf("%d",&a[i]);
			ans+=Query(a[i]+1,n,1,n,1);
//			cout<<ans<<" ";
			Update(1,n,a[i]+1,1);
		}int res=ans;
		for(int i=1;i<=n;++i){
			res-=Query(1,a[i]+1,1,n,1)-1;
			res+=Query(a[i]+1,n,1,n,1)-1;
//			cout<<res<<" ";
			ans=min(ans,res);
		}printf("%d\n",ans);
	}
}
/*

*/

CF#510 Div.2 D. Petya and Array(前缀和,逆序数)

题目链接:http://codeforces.com/contest/1042/problem/D

题目大意:区间中选择一些连续的区间,满足 \sum_{i=l}^{r}a[i]<t,然后看有多少个这样的区间。

思路:满足条件sum[r]-sum[l-1]<t。

ACCode:

// luogu-judger-enable-o2
//#pragma comment(linker, "/STACK:1024000000,1024000000")
 
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<time.h>
 
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define ll long long
#define Pair pair<ll,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// 水印
//std::ios::sync_with_stdio(false);
//  register
const int MAXN=2e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double EPS=1.0e-8;
const double PI=acos(-1.0);

ll Tree[MAXN<<2];
ll a[MAXN],sum[MAXN];
ll n,t;

int get_r(ll res){
	int l=1,r=n,mid;
	while(l<=r){//find the last r to sum[l-1]>sum[r]-t  => res>sum[r]
		mid=(l+r)>>1;
		if(res>sum[mid]) l=mid+1;
		else r=mid-1;
	}return r;
}
int get_l(ll res){
	int l=1,r=n,mid;
	while(l<=r){
		mid=(l+r)>>1;
		if(res==sum[mid]) return mid;
		else if(res>sum[mid]) l=mid+1;
		else r=mid-1;
	}
}
void Update(int l,int r,int x,int oper,int rt){//修改一个点 
	if(l==r){
		Tree[rt]+=oper;
		return ;
	}int mid=(l+r)>>1;
	if(x<=mid) Update(l,mid,x,oper,rt<<1);
	else Update(mid+1,r,x,oper,rt<<1|1);
	Tree[rt]=Tree[rt<<1]+Tree[rt<<1|1];
}
int Query(int ql,int qr,int l,int r,int rt){
	if(qr==0) return 0;
	if(l>=ql&&r<=qr){
		return Tree[rt];
	}int mid=(l+r)>>1;
	int ans=0;
	if(ql<=mid) ans+=Query(ql,qr,l,mid,rt<<1);
	if(qr>mid) ans+=Query(ql,qr,mid+1,r,rt<<1|1);
	return ans;
}
int main(){
	while(~scanf("%lld%lld",&n,&t)){
		clean(Tree,0);
		for(int i=1;i<=n;++i){
			scanf("%lld",&a[i]);
			sum[i]=sum[i-1]+a[i];
		}sort(sum+1,sum+1+n);
		for(int i=1;i<=n;++i){
			sum[i]=sum[i]-t;
		}sort(sum+1,sum+1+n);
		for(int i=1;i<=n;++i){
			Update(1,n,i,1,1);
		}ll ans=0,res=0;
		for(int i=1;i<=n;++i){
			int r=get_r(res);
//			cout<<r<<" : ";
			ans+=Query(1,r,1,n,1);
//			cout<<ans<<" ";
			res+=a[i];
			int l=get_l(res-t);
			Update(1,n,l,-1,1);
		}printf("%lld\n",ans);
	}
}
/*

*/

猜你喜欢

转载自blog.csdn.net/qq_40482358/article/details/89192575