联赛模拟测试5 序列 主席树

题目描述

分析

这道题把修改操作强制在线,但是查询却可以离线处理

对于每一个位置,我们开一棵权值线段树,记录这个位置上的每一个取值对答案的贡献

对于每一次询问,对它有贡献的点是区间([l,r])中大于等于(x)的数

因此我们考虑差分,在(l)的位置(+1),在(r+1)的位置(-1)

转换到具体的点上,就是当点的坐标恰好为(l),在(x)的位置(+1)

恰好为(r+1),在(x)的位置(-1)

修改时减去旧的贡献加上新的贡献即可

代码

#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
inline int read(){
	int x=0,fh=1;
	char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e5+5;
int n,m,q,cnt,a[maxn],rt[maxn],latans;
std::vector<int> jll[maxn],jlr[maxn];
struct trr{
	int lc,rc,w;
}tr[maxn*40];
int ad(int da,int lat,int l,int r,int wz,int val){
	da=++cnt;
	tr[da]=tr[lat];
	if(l==r){
		tr[da].w+=val;
		return da;
	}
	int mids=(l+r)>>1;
	if(wz<=mids) tr[da].lc=ad(tr[da].lc,tr[lat].lc,l,mids,wz,val);
	else tr[da].rc=ad(tr[da].rc,tr[lat].rc,mids+1,r,wz,val);
	tr[da].w=tr[tr[da].lc].w+tr[tr[da].rc].w;
	return da;
}
int cx(int da,int l,int r,int x,int y){
	if(l>=x && r<=y) {
		return tr[da].w;
	}
	int mids=(l+r)>>1;
	if(y<=mids) return cx(tr[da].lc,l,mids,x,y);
	else if(x>mids) return cx(tr[da].rc,mids+1,r,x,y);
	return cx(tr[da].lc,l,mids,x,y)+cx(tr[da].rc,mids+1,r,x,y);
}
int main(){
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	n=read(),m=read(),q=read();
	latans=0;
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	for(int i=1;i<=m;i++){
		int l,r,x;
		l=read(),r=read(),x=read();
		jlr[r+1].push_back(x);
		jll[l].push_back(x);
	}
	for(int i=1;i<=n+1;i++){
		rt[i]=rt[i-1];
		for(int j=0;j<jlr[i].size();j++){
			rt[i]=ad(rt[i],rt[i],1,n,jlr[i][j],-1);
		}
		for(int j=0;j<jll[i].size();j++){
			rt[i]=ad(rt[i],rt[i],1,n,jll[i][j],1);
		}
		if(i<=n) latans+=cx(rt[i],1,n,1,a[i]);
	}
	printf("%d
",latans);
	for(int i=1;i<=q;i++){
		int p,v;
		p=read(),v=read();
		p^=latans,v^=latans;
		latans+=cx(rt[p],1,n,1,v)-cx(rt[p],1,n,1,a[p]);
		a[p]=v;
		printf("%d
",latans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/liuchanglc/p/13721165.html