权值线段树

权值线段树的本质是线段树维护桶。

权值线段树维护一个区间内出现数的个数。


和普通线段树的区别

权值线段树维护的是桶,按值域开空间,维护的是个数。

简单线段树维护的是信息,按个数可开空间,维护的是特定信息。


用途

  1. 求一段区间内数出现的次数

  2. 比 x 小的数的个数

  3. 比 x 大的数的个数

  4. 区间第 k 大的数

  5. 区间第 k 小的数。


一般和动态开点一块使用,可以大大减少时间和空间。

例题

普通平衡树

见code:

const int N=1e6;
const int INF=1e7;
int n, op, x, tot, root;
struct node{
	int dat,l,r;
}t[N<<2];

il int read(){
	int f = 1, x = 0; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')f = -f;ch = getchar();}
	while(ch >= '0' && ch <= '9'){x = x*10 + ch-'0';ch = getchar();}
	return f * x;
}

void pushup(int k){t[k].dat=t[t[k].l].dat+t[t[k].r].dat;}

void Insert(int &pos,int l,int r,int x,int val) {//加&相当于引用。 
	if(!pos) {pos=++tot;}//一边找一边更新左右儿子。 
	if(l==r) {
		t[pos].dat+=val;
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid)
		Insert(t[pos].l,l,mid,x,val);
	else
		Insert(t[pos].r,mid+1,r,x,val);
	pushup(pos);
}

int query_kth(int k,int l,int r,int fl,int fr){
	if(!k) return 0;
	if(l>=fl&&r<=fr) return t[k].dat;
	int res=0,mid=(l+r)>>1;
	if(fl<=mid)
		res+=query_kth(t[k].l,l,mid,fl,fr);
	if(fr>mid)
		res+=query_kth(t[k].r,mid+1,r,fl,fr);
	return res;
}

int kth(int k,int l,int r,int x){
	if(!k) return -1;
	if(l==r) return l;
	int mid=(l+r)>>1,left=t[t[k].l].dat;
	if(x<=left)
		return kth(t[k].l,l,mid,x);
	return kth(t[k].r,mid+1,r,x-left);
}

signed main(){
	n = read();
	while(n--) {
		op = read(); x = read();
		if(op==1) {//插入 
			Insert(root,-INF,INF,x,1);
		}
		else if(op==2) {//删除 
			Insert(root,-INF,INF,x,-1);
		}
		else if(op==3) {//查询k的排名 
			cout<<query_kth(root,-INF,INF,-INF,x-1)+1<<'\n';//查询-1e7~x-1有多少数,然后+1 
		}
		else if(op==4) {//查询排名为k的数 
			cout<<kth(root,-INF,INF,x)<<'\n';
		}
		else if(op==5) {//求x的前驱 
			int p=query_kth(root,-INF,INF,-INF,x-1);
			cout<<kth(root,-INF,INF,p)<<'\n';
		}
		else {//求x的后继 
			int p=query_kth(root,-INF,INF,-INF,x)+1;
			cout<<kth(root,-INF,INF,p)<<'\n';
		}
	}
	return 0;
}

Petya and Array

/*
求有多少区间和 < t
转化为
Q[r]-Q[l-1]<t
Q[l-1]>Q[r]-t
用权值线段树维护前缀和
枚举Q[r] 
*/
const int N=1e6+10;
const int M=1e7+10;

int n, len, x, ans, root, tot, Q[N];
struct node{
	int dat,l,r;
}t[M];

il int read(){
	int f = 1, x = 0; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')f = -f;ch = getchar();}
	while(ch >= '0' && ch <= '9'){x = x*10 + ch-'0';ch = getchar();}
	return f * x;
}

void pushup(int k){t[k].dat=t[t[k].l].dat+t[t[k].r].dat;} 

void Insert(int &k,int l,int r,int x){
	if(!k) {k=++tot;}
	if(l==r){
		t[k].dat+=1;
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid)
		Insert(t[k].l,l,mid,x);
	else Insert(t[k].r,mid+1,r,x);
	pushup(k);
}

int Query(int k,int l,int r,int fl,int fr) {//查询比Q[i]大的个数
	if(!k) return 0;
	if(l>=fl&&r<=fr) return t[k].dat;
	int res=0,mid=(l+r)>>1;
	if(fl<=mid)
		res+=Query(t[k].l,l,mid,fl,fr);
	if(fr>mid)
		res+=Query(t[k].r,mid+1,r,fl,fr);
	return res; 
}

signed main(){
	n = read();len = read();
	rep(i, 1, n) {
		x = read();
		Q[i]=Q[i-1]+x;
		Insert(root,-inf,inf,Q[i-1]);
		ans+=Query(root,-inf,inf,Q[i]-len+1,inf);
	}
	cout<<ans;
	return 0;
}

Physical Education Lessons

这题卡longlong。而且卡数组大小。

/*
动态开点+线段树+区间修改+区间求和 
*/
const int N=15000010;
int n, q, op, l, r, tot=1, root=1;
struct node{
	int dat,l,r,lazy;
}t[N];

il int read(){
	int f = 1, x = 0; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')f = -f;ch = getchar();}
	while(ch >= '0' && ch <= '9'){x = x*10 + ch-'0';ch = getchar();}
	return f * x;
}

void pushup(int k){t[k].dat=t[t[k].l].dat+t[t[k].r].dat;}

void pushdown(int k,int l,int r){
	if(t[k].lazy==-1) return ;
	int mid=(l+r)>>1;
	if(!t[k].l){t[k].l=++tot;t[t[k].l].lazy=-1;}
	if(!t[k].r){t[k].r=++tot;t[t[k].r].lazy=-1;}
	t[t[k].l].dat=(mid-l+1)*t[k].lazy;
	t[t[k].l].lazy=t[k].lazy;
	t[t[k].r].dat=(r-(mid+1)+1)*t[k].lazy;
	t[t[k].r].lazy=t[k].lazy;
	t[k].lazy=-1;
	return ;
}

void Insert(int &k, int l, int r,int fl,int fr,int val){//0是工作日 1是非工作日 
	if(!k) {k=++tot;t[k].lazy=-1;}
	if(l>=fl&&r<=fr) {
		t[k].dat=(r-l+1)*val;
		t[k].lazy=val;
		return ;
	}
	pushdown(k,l,r);
	int mid=(l+r)>>1;
	if(fl<=mid)
		Insert(t[k].l,l,mid,fl,fr,val);
	if(fr>mid)
		Insert(t[k].r,mid+1,r,fl,fr,val);
	pushup(k);
}

signed main() {
	n = read();
	q = read();
	rep(i, 1, q){
		l = read();r = read();op = read();
		Insert(root,1,n,l,r,(op==1)?1:0);
		cout<<(n-t[1].dat)<<'\n';
	}
	return 0;
}

郁闷的出货员

这个题有个特ex的地方就是字符串输入卡scanfgetchar

一开始输入的时候用了getchar(),交上去一个点也没对,下载了一个数据结果发现输出没有错误。然后翻讨论区发现“用cin试试”。

得到一个\(trick\):字符串类的输入尽量用 cin,正确性高。

const int N=3e5+10;
const int B=1e5;
int n,M,x,tt,tot,ans,root,least,chg;
char op;
struct node{
	int dat,l,r;
}t[N];

il int read(){
	int f = 1, x = 0; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')f = -f;ch = getchar();}
	while(ch >= '0' && ch <= '9'){x = x*10 + ch-'0';ch = getchar();}
	return f * x;
}

void pushup(int k){t[k].dat=t[t[k].l].dat+t[t[k].r].dat;}

void Insert(int &k,int l,int r,int x){
	if(!k) k=++tot;
	if(l==r){
		t[k].dat++;
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid)
		Insert(t[k].l,l,mid,x);
	else Insert(t[k].r,mid+1,r,x);
	pushup(k);
}

int Query(int k,int l,int r,int fl,int fr){
	if(l>=fl&&r<=fr){
		return t[k].dat;
	}
	int mid=(l+r)>>1,ans=0;
	if(fl<=mid)
		ans+=Query(t[k].l,l,mid,fl,fr);
	if(fr>mid)
		ans+=Query(t[k].r,mid+1,r,fl,fr);
	return ans;
}

void Change(int k,int l,int r,int fl,int fr){
	if(l==r){
		t[k].dat=0;
		return ;
	}
	int mid=(l+r)>>1;
	if(t[t[k].l].dat&&fl<=mid) Change(t[k].l,l,mid,fl,fr);
	if(t[t[k].r].dat&&fr>mid) Change(t[k].r,mid+1,r,fl,fr);
	pushup(k);
}

int Kth(int k,int l,int r,int x){
	if(!k) return -1;
	if(l==r) return l;
	int mid=(l+r)>>1;
	if(t[t[k].r].dat>=x) return Kth(t[k].r,mid+1,r,x);
	return Kth(t[k].l,l,mid,x-t[t[k].r].dat);
}

signed main(){
	n=read();M=read();
	least=M+B;
	rep(i,1,n){
		cin>>op;x=read();
		if(op=='I') {
			if(x<M) continue;
			Insert(root,0,inf,x-chg+B);
		}
		else if(op=='A'){
			least-=x;
			chg+=x;
		}
		else if(op=='S'){
			least+=x;
			chg-=x;
			if(least>=1&&Query(root,0,inf,0,least-1)>0){
				ans+=Query(root,0,inf,0,least-1);
				Change(root,0,inf,0,least-1);
			}
		}
		else if(op=='F'){
			if(x>Query(root,0,inf,least,inf)) cout<<"-1"<<'\n';
			else cout<<Kth(root,0,inf,x)-B+chg<<'\n';
		}
	}
	cout<<ans<<'\n';
	return 0;
}
posted @ 2023-06-19 19:40  XYini  阅读(4)  评论(0)    收藏  举报