树状数组套权值线段树 学习笔记

树状数组套权值线段树

首先要知道权值线段可以处理全局第 K 大;主席树可以处理静态区间第 K 大,所谓静态就是不能改变值;但是如果有修改操作,用主席树做,每次要修改,后面的所有主席树都要进行一次修改,复杂度为 O(n); 显然是不行的;

这里就可以用到树状数组维护前缀和,权值线段树维护区间信息;

换句话说就是树状数组每个结点换成权值线段树,主体思想还是跟主席树一样,利用前缀和求某个区间第 K 大(或者其他操作);


全部代码都是这道题:

Dynamic Rankings

个人认为最难理解的就是建树部分,这建树还是跟主席树一样的思想,只是第 i 颗树不再是第 i-1 颗树转化过来,而是第 i 颗树转化过来,也就是说当你更新第 i 颗树时,第 i 棵树要从原来的第 i 颗树转化过来;

例如:更新第 i 棵树的某个值,只要在原来的第 i 棵树上再加一条链就行;跟主席树的思路还是一样的;

int update(int pre,int l,int r,int pos,int s){
	int rt=++tot;
	L[rt]=L[pre],R[rt]=R[pre],sum[rt]=sum[pre]+s;
	int d=(l+r)>>1;
	if(l<r){
		if(pos<=d) L[rt]=update(L[pre],l,d,pos,s);
		else R[rt]=update(R[pre],d+1,r,pos,s);
	}
	return rt;
}
void add(int p,int q){//树状数组操作
	int pos=lower_bound(b+1,b+cnt+1,a[p])-b;
	while(p<=cnt){
		tr[p]=update(tr[p],1,cnt,pos,q);
		p+=lowbit(p);
	}
}

查询操作,跟主席树的思路还是一样的,只不过不再是 sum[L[nw]]-sum[L[pre]];(这里的nw代表第 r 棵树的根结点,pre代表第 l 棵树的根结点);

而是根据树状数组求出要算的 l ,和要算的 r,然后循环加上减去就行;

代码:

int query(int l,int r,int k){
	if(l==r) return l;
	int s=0;
	int d=(l+r)>>1;
	for(int i=0;i<vr.size();i++) s+=sum[L[vr[i]]];
	for(int i=0;i<vl.size();i++) s-=sum[L[vl[i]]];
	if(s>=k){
		for(int i=0;i<vr.size();i++) vr[i]=L[vr[i]];
		for(int i=0;i<vl.size();i++) vl[i]=L[vl[i]];
		return query(l,d,k);
	}
	else{
		for(int i=0;i<vr.size();i++) vr[i]=R[vr[i]];
		for(int i=0;i<vl.size();i++) vl[i]=R[vl[i]];
		return query(d+1,r,k-s);
	}
}

总代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=100100;
const int M=10000000;
const LL mod=100000000;
int n,m,cnt,tot,a[N*2],b[N*2],c[N*2],tr[N*2],L[N*400],R[N*400],sum[N*400];
struct Node{
	char s[10];
	int l,r,k;
}que[N];//问题 
vector<int>vl,vr;
int lowbit(int k){ return k & (-k); }
int update(int pre,int l,int r,int pos,int s){
	int rt=++tot;
	L[rt]=L[pre],R[rt]=R[pre],sum[rt]=sum[pre]+s;
	int d=(l+r)>>1;
	if(l<r){
		if(pos<=d) L[rt]=update(L[pre],l,d,pos,s);
		else R[rt]=update(R[pre],d+1,r,pos,s);
	}
	return rt;
}
void add(int p,int q){
	int pos=lower_bound(b+1,b+cnt+1,a[p])-b;
	while(p<=cnt){
		tr[p]=update(tr[p],1,cnt,pos,q);
		p+=lowbit(p);
	}
}
int query(int l,int r,int k){
	if(l==r) return l;
	int s=0;
	int d=(l+r)>>1;
	for(int i=0;i<vr.size();i++) s+=sum[L[vr[i]]];
	for(int i=0;i<vl.size();i++) s-=sum[L[vl[i]]];
	if(s>=k){
		for(int i=0;i<vr.size();i++) vr[i]=L[vr[i]];
		for(int i=0;i<vl.size();i++) vl[i]=L[vl[i]];
		return query(l,d,k);
	}
	else{
		for(int i=0;i<vr.size();i++) vr[i]=R[vr[i]];
		for(int i=0;i<vl.size();i++) vl[i]=R[vl[i]];
		return query(d+1,r,k-s);
	}
}
int main(){
//    ios::sync_with_stdio(false);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
    	scanf("%d",&a[i]);
    	b[i]=a[i];
	}
	int ge=n;
	for(int i=1;i<=m;i++){
		scanf("%s",que[i].s);
		if(que[i].s[0]=='Q') scanf("%d%d%d",&que[i].l,&que[i].r,&que[i].k);
		else scanf("%d%d",&que[i].l,&que[i].r);b[++ge]=que[i].r;
	}
	sort(b+1,b+ge+1);
	cnt=unique(b+1,b+ge+1)-b-1;
	for(int i=1;i<=n;i++) add(i,1);
	for(int i=1;i<=m;i++){
		if(que[i].s[0]=='Q'){
			vl.clear(),vr.clear();
			que[i].l-=1;
			while(que[i].l){//预处理要求的树 
				vl.push_back(tr[que[i].l]);
				que[i].l-=lowbit(que[i].l);
			}
			while(que[i].r){//预处理要求的树 
				vr.push_back(tr[que[i].r]);
				que[i].r-=lowbit(que[i].r);
			}
			printf("%d\n",b[query(1,cnt,que[i].k)]);
		}
		else{
			add(que[i].l,-1);
			a[que[i].l]=que[i].r;
			add(que[i].l,1);
		}
	}
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值