【权值线段树】
学习权值线段树,首先要了解线段树是什么。如果不会的可以先学习一下。
是什么
权值线段树,顾名思义是一棵线段树。
但它和普通线段树不同:
线段树,每个节点用来维护一段区间的最大值或总和等。
权值线段树,相当于一个桶,每个节点用来表示一个区间的数***出现的次数***。
为什么要用它
我们可以用它来维护一段区间的数出现的次数,从它的定义上来看,它可以快速计算一段区间的数的出现次数。
此外,它还有一个重要功能,在于它可以快速找到第k kk大或第k kk小值,下面会做详细解释。
其实,它就是一个桶,桶能做到的它都可以用更快的速度去完成。
基本操作
添加
和普通线段树类似,递归到叶子节点时给f[v]+1 f[v]+1f[v]+1。
以下代码要添加的数是x xx,也就是x xx出现的次数+1 +1+1。
一道简单模板题;
也记记吧,免得脑子不灵光又忘记了。
洛谷 1168
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int n;
int a[N],b[N];
struct Tree
{
int l,r,mid;
int num;
}tree[N<<2];
int read()
{
char c=getchar();int num=0;
for(;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())
num=num*10+c-'0';
return num;
}
void build(int root,int l,int r)
{
tree[root].l=l,tree[root].r=r,tree[root].mid=l+r>>1;
if(l==r)
return;
build(root<<1,l,tree[root].mid);
build(root<<1|1,tree[root].mid+1,r);
}
void update(int root,int x)
{
++tree[root].num;
if(tree[root].l==tree[root].r)
return;
if(x<=tree[root].mid)
update(root<<1,x);
else
update(root<<1|1,x);
}
int query(int root,int num)
{
if(tree[root].l==tree[root].r)
return tree[root].l;
if(num<=tree[root<<1].num)
return(query(root<<1,num));
else
return(query(root<<1|1,num-tree[root<<1].num));
}
int main()
{
n=read();
for(int i=1;i<=n;++i)
a[i]=read(),b[i]=a[i];
sort(b+1,b+n+1);
int bound=unique(b+1,b+n+1)-b;
build(1,1,n);
for(int i=1;i<=n;++i){
int pos=lower_bound(b+1,b+bound+1,a[i])-b;
update(1,pos);
if(i%2)
printf("%d\n",b[query(1,i/2+1)]);
}
return 0;
}
hdu 6703 array
题目链接http://acm.hdu.edu.cn/showproblem.php?pid=6703
题目大意:
给出一个n(n<1e5)个元素的数组A,A中所有元素都是不重复的[1,n]。
有两种操作:
1.将pos位置的元素+1e7
2.查询不属于[1,r]中的最小的>=k的值。
强制在线,上次计算结果和输入值xor得到区间。
比赛的时候感觉这道题有点线段树的感觉,和前段时间多校训练一个题很像,想了40多分钟才理想清思路。
解法:
可以看出,执行1操作的时候加的数非常大,可以得出每次输出的答案在为1到n+1之间。可以将1-n的每个数在数组A中的位置记录下,存在线段树中,维护线段树区间最大值。
当执行2操作时,只需要查询区间[k,n]中大于r的值即可,找出最小的数字,先搜索左子树,若无答案搜索右子树,若两侧均无解则答案为n+1。
当执行1操作时,由于pos+1e7,因此之后的数组中不存在该数,将第pos位的数字在线段树中的值转换为inf即可。
#include<bits/stdc++.h> using namespace std; const int maxx=100005; const int inf=0x3f3f3f3f; int T,n,m,a[maxx],c[maxx],r,k,x,ans; struct node { int l,r,v; }Tr[maxx*4]; void build(int id,int l,int r) { Tr[id].l=l,Tr[id].r=r; if(l==r){Tr[id].v=c[l];return;} int mid=(l+r)/2; build(id*2,l,mid); build(id*2+1,mid+1,r); Tr[id].v=max(Tr[id*2].v,Tr[id*2+1].v); } void update(int id,int num) { if(Tr[id].l==Tr[id].r) {Tr[id].v=inf;return;} int mid=(Tr[id].l+Tr[id].r)/2; if(num<=mid)update(id*2,num); else update((id*2+1),num); Tr[id].v=max(Tr[id*2].v,Tr[id*2+1].v); } int query(int id,int r,int k) { if(Tr[id].l==Tr[id].r){ if(Tr[id].v>r) return Tr[id].l; else return -1; } int mid=(Tr[id].l+Tr[id].r)/2,ans=-1; if(mid>=k&&Tr[id*2].v>r) ans=query(id*2,r,k); if(ans!=-1) return ans; else if(Tr[id*2+1].r>=k&&Tr[id*2+1].v>r) ans=query(id*2+1,r,k); return ans; } int main() { scanf("%d",&T); while(T--){ ans=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]),c[a[i]]=i; build(1,1,n); while(m--){ scanf("%d",&x); if(x==2){scanf("%d %d",&r,&k);ans=query(1,r^ans,k^ans);if(ans==-1)ans=max(n+1,k);printf("%d\n",ans);} else{scanf("%d",&x);update(1,a[x^ans]);} } } }