权值线段树(知识学习+板子总结)

概念

和线段树一样的函数,

p这个区间里维护的是这段区间里的数出现的次数

每出现一个数x,叶子结点[x,x]这个区间就+1,删除就-1

支持动态查询第k大的数、数x出现的最小rank、

寻找x的前驱(即小于x的最大的数)

和寻找x的后继(即大于x的最小的数)等操作

思路来源

https://www.bilibili.com/video/av16552942?from=search&seid=1412104890759122013

【算法讲堂】【电子科技大学】【ACM】权值线段树与主席树

心得

当学主席树的预科知识

主席树还是结构体的好用

但平时写参的写习惯了一时半会改不过来

而且注释标明了 会比较好理解 忘了方便重学

不像网上某些代码可读性极差

 

其实装到一个结构体里会比较好用

到时候一套板子就成了当黑科技用

不过面向过程的acm编程就不在乎这么多了

 

主席树回头学明白了再总结

代码这么多以后就不手抄了 用博客学新知识

课后例题

BZOJ1503 郁闷的出纳员

代码板子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
const int maxn=1e5+10;
int num[maxn*5];
//权值线段树: 
//区间的值是这段值域里的个数的线段树 
//叶子结点的值 是这个数在序列里出现的次数
//可以当平衡树用 比平衡树代码好写 
/* 
没有必要build
多组样例的时候
memset就搞定了 
void build(int p,int l,int r)
{
	if(l==r)
	{
		num[l]=0;
		return;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
} 
*/
void update(int p,int l,int r,int v,int op)//op==1或-1,插入或删除 
{
	num[p]+=op;
	if(l==r)return;
	int mid=(l+r)>>1;
	if(v<=mid)update(p<<1,l,mid,v,op);
	else update(p<<1|1,mid+1,r,v,op); 
}

int Kth(int p,int l,int r,int rank)//k小值 
{
	if(l==r)return l;
	int mid=(l+r)>>1;
	if(num[p<<1]>=rank)return Kth(p<<1,l,mid,rank);//左子k小 
	return Kth(p<<1|1,mid+1,r,rank-num[p<<1]);//右子(k-左num)小 
} 

//求一个数的最小排名,排名从0起 
int Rank(int p,int l,int r,int v)//[1,v-1]的出现个数 即v-1>mid 即前面3个数v就rank3 
{
	if(r<v)return num[r];
	int mid=(l+r)>>1,res=0;
	if(v>l)res+=Rank(p<<1,l,mid,v);//左段区间得有比v小的值,才有加的意义,比如说rank[1]=0 
	if(v>mid+1)res+=Rank(p<<1|1,mid+1,r,v);//右段区间得有比v小的值,才有加的意义 
	return res;
} 

int Findpre(int p,int l,int r)
{
	if(l==r)return l;
	int mid=(l+r)>>1;
	if(num[p<<1|1])return Findpre(p<<1|1,mid+1,r);//右子树非空向右找 
	return Findpre(p<<1,l,mid);//否则向左找 
}
//找前驱 尽可能在小于v的右子树找 
int Pre(int p,int l,int r,int v)
{
	if(r<v)//maxr<v即在p的子树中 p区间内最右非空子树即答案 
	{
		if(num[p])return Findpre(p,l,r);
		return 0;
	}
	int mid=(l+r)>>1,Re;
	//如果v在右子树可能有前驱(至少mid+1比v小)就先查右子树,l=mid+1 
	if(mid+1<v&&num[p<<1|1]&&(Re=Pre(p<<1|1,mid+1,r,v)))return Re;
	//否则查左子树,r=mid,使r不断变小直至满足题意小于v 
	return Pre(p<<1,l,mid,v);
} 

int Findnext(int p,int l,int r)
{
	if(l==r)return l;
	int mid=(l+r)>>1;
	if(num[p<<1])return Findnext(p<<1,l,mid);
	return Findnext(p<<1|1,mid+1,r);
} 

//找后继 尽可能在大于v的左子树找 
int Next(int p,int l,int r,int v)
{
	if(v<l)//已找到大于v的最小完整区间 
	{
		if(num[p])return Findnext(p,l,r); 
		return 0;
	}
	int mid=(l+r)>>1,Re;
	//如果左子树里有比v大的(至少mid比v大)就查左子树 否则查右子树 
	if(v<mid&&num[p<<1]&&(Re=Next(p<<1,l,mid,v)))return Re;
	return Next(p<<1|1,mid+1,r,v);
}

int main()
{
	return 0;
} 

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值