[Ynoi2007] rgxsxrs 题解

Description

用一个数据结构维护一个序列 \(a_i\) ,使得能够:

1.将一个区间内所有大于一个数(设为 \(x\) )的值全部减去这个数

2.求一个区间的和,最大值和最小值

\(1\leq n,m\leq 5\cdot 10^5\ \ \ 1\leq a_i\leq 10^9\)

Solution

大佬们有说分块套 splay 可以过,但是我做不来,所以还是来讲一讲比较普遍的做法吧。。

首先,对于维护一个序列,我们应该能比较容易能够想到用线段树去维护区间。

然后在此基础上,我们看看怎么去暴力修改,

当我们搜到一个区间的时候,会分三种情况:

1.如果最大值都比 \(x\) 小,直接跳过;

2.如果最小值都比 \(x\) 大,打个懒标记就跑;

3.就是其他的情况,有大的,有小的,就只能递归下去,最终一直到前两种的其中一种情况;

如果我们这么来看,时间复杂度要废,就比如每次给定的 \(x\) 能让整个区间交替着一大一小,那么每次递归都会到叶子节点,所以直接硬上约等于暴力,考虑可以怎么解决。

从值域出发,考虑对值域分块,但直接上 \(10^9\) 的分块似乎不太有用,但指数的爆炸性增长可以解决。所以我们可以考虑按照 \([w^k,w^{k+1})\) 这样来分块.

这样只对值域在这一块的值进行上面的操作,就不会出现无限细分的情况。

然后注意操作完后可能有些值会向更小的块掉落,但最坏可能都是 \(\log\) 级别的,时间复杂度上就解决了。

但是,这样做空间是 \(n\cdot \log\) 级别的,善(du)良(liu)的出题人只给了我们 64MB ,所以我们还得来优化一下空间。

想到叶子节点的利用率比较低,甚至可以暴力操作,所以我们可以不存线段树中长度小于 \(k\) 的序列,从而直接暴力,以比较小代价的时间换取空间。

至此,我们以线性的空间完成了这道题。

注:这里面 \(w\) 我取了 \(16\)\(K\) 取了 \(32\) ,当然其他的 \(w\)\(k\) 都各有千秋,这个看自己怎么取。

#include<bits/stdc++.h>
#define num(id,x) seg[id].num[x]
#define sum(id,x) seg[id].sum[x]
#define lzy(id,x) seg[id].lzy[x]
#define imax(id,x) seg[id].imax[x]
#define imin(id,x) seg[id].imin[x]
#define lt(id) seg[id].lt
#define rt(id) seg[id].rt
using namespace std;
typedef long long ll;
const int N=5e5+10,M=4e4+10,W=16,K=32;
const int INF=2e9,last=(1<<20);
int n,m,a[N],lastans,maxx,minn;
ll ans;
struct mdzz{
	ll num[M],sum[M],lzy[M],lt,rt;
	int imax[M],imin[M];
}seg[9];
inline ll read(){
	ll s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
	return s*w;
}
inline void print(ll x){
	if(x<0){
		x=-x;
		putchar('-');
	}
	if(x>9)print(x/10);
	putchar(x%10+48);
}
inline int belong(ll x){
	int now=1;
	while(x>rt(now))++now;
	return now;
}
inline void pushup_blo(int it,int now,int lf,int rg){
	num(it,now)=sum(it,now)=imax(it,now)=0;
	imin(it,now)=INF;
	for(int i=lf;i<=rg;++i){
		if(lt(it)<=a[i]&&a[i]<=rt(it)){
			++num(it,now);
			sum(it,now)+=a[i];
			imax(it,now)=max(imax(it,now),a[i]);
			imin(it,now)=min(imin(it,now),a[i]);
		}
	}
}
inline void pushdown_blo(int it,int lf,int rg,ll val){
	for(int i=lf;i<=rg;++i){
		if(lt(it)<=a[i]&&a[i]<=rt(it))a[i]-=val;
	}
}
inline void pushup(int it,int now){
	num(it,now)=num(it,now<<1)+num(it,now<<1|1);
	sum(it,now)=sum(it,now<<1)+sum(it,now<<1|1);
	imax(it,now)=max(imax(it,now<<1),imax(it,now<<1|1));
	imin(it,now)=min(imin(it,now<<1),imin(it,now<<1|1));
}
inline void pushdown(int it,int now){
	if(lzy(it,now)){
		if(num(it,now<<1)){
			lzy(it,now<<1)+=lzy(it,now);
			imax(it,now<<1)-=lzy(it,now);
			imin(it,now<<1)-=lzy(it,now);
			sum(it,now<<1)-=lzy(it,now)*num(it,now<<1);
		}
		if(num(it,now<<1|1)){
			lzy(it,now<<1|1)+=lzy(it,now);
			imax(it,now<<1|1)-=lzy(it,now);
			imin(it,now<<1|1)-=lzy(it,now);
			sum(it,now<<1|1)-=lzy(it,now)*num(it,now<<1|1);
		}
		lzy(it,now)=0;
	}
}
inline void insert(int it,int now,int lf,int rg,int pos,int val){
	if(lf+K>rg){
		if(lzy(it,now)){
			pushdown_blo(it,lf,rg,lzy(it,now));
			lzy(it,now)=0;
		}
		a[pos]=val;
		pushup_blo(it,now,lf,rg);
		return ;
	}
	int mid=(lf+rg)>>1;
	pushdown(it,now);
	if(pos<=mid)insert(it,now<<1,lf,mid,pos,val);
	else insert(it,now<<1|1,mid+1,rg,pos,val);
	pushup(it,now);
}
inline void update_blo(int it,int lf,int rg,int lef,int rig,int val){
	int left=max(lf,lef),rigt=min(rg,rig);
	for(int i=left;i<=rigt;++i){
		if(lt(it)<=a[i]&&a[i]<=rt(it)&&a[i]>val){
			a[i]-=val;
			if(a[i]<lt(it))insert(belong(a[i]),1,1,n,i,a[i]);
		}
	}
}
inline void query_blo(int it,int lf,int rg,int lef,int rig){
	int left=max(lf,lef),rigt=min(rg,rig);
	for(int i=left;i<=rigt;++i){
		if(lt(it)<=a[i]&&a[i]<=rt(it)){
			ans+=a[i];
			maxx=max(maxx,a[i]);
			minn=min(minn,a[i]);
		}
	}
}
inline void update(int it,int now,int lf,int rg,int lef,int rig,int val){
	if(imax(it,now)<=val)return ;
	if(lf+K>rg){
		if(lzy(it,now)){
			pushdown_blo(it,lf,rg,lzy(it,now));
			lzy(it,now)=0;
		}
		update_blo(it,lf,rg,lef,rig,val);
		pushup_blo(it,now,lf,rg);
		return ;
	}
	if(lef<=lf&&rg<=rig){
		if(lt(it)+val<=imin(it,now)){
			lzy(it,now)+=val;
			imax(it,now)-=val;
			imin(it,now)-=val;
			sum(it,now)-=val*num(it,now);
			return ;
		}
	}
	int mid=(lf+rg)>>1;
	pushdown(it,now);
	if(lef<=mid)update(it,now<<1,lf,mid,lef,rig,val);
	if(rig>mid)update(it,now<<1|1,mid+1,rg,lef,rig,val);
	pushup(it,now);
}
inline void query(int it,int now,int lf,int rg,int lef,int rig){
	if(lef<=lf&&rg<=rig){
		ans+=sum(it,now);
		maxx=max(maxx,imax(it,now));
		minn=min(minn,imin(it,now));
		return ;
	}
	if(lf+K>rg){
		if(lzy(it,now)){
			pushdown_blo(it,lf,rg,lzy(it,now));
			lzy(it,now)=0;
		}
		query_blo(it,lf,rg,lef,rig);
		pushup_blo(it,now,lf,rg);
		return ;
	}
	int mid=(lf+rg)>>1;
	pushdown(it,now);
	if(lef<=mid)query(it,now<<1,lf,mid,lef,rig);
	if(rig>mid)query(it,now<<1|1,mid+1,rg,lef,rig);
	pushup(it,now);
}
int main(){
	n=read();m=read();
	for(int i=1;i<=8;++i){
		lt(i)=rt(i-1)+1;rt(i)=lt(i)*W-1;
		for(int j=0;j<M;++j){
			imax(i,j)=0;imin(i,j)=INF;
		}
	}
	for(int i=1;i<=n;++i){
		a[i]=read();
		insert(belong(a[i]),1,1,n,i,a[i]);
	}
	for(int i=1;i<=m;++i){
		int opt=read(),l=read()^lastans,r=read()^lastans;
		if(opt==1){
			int x=read()^lastans;
			for(int j=1;j<=8;++j)if(rt(j)>=x){
				update(j,1,1,n,l,r,x);
			}
		}
		else if(opt==2){
			ans=maxx=0;minn=INF;
			for(int j=1;j<=8;++j){
				query(j,1,1,n,l,r);
			}
			print(ans);printf(" ");
			print(minn);printf(" ");
			print(maxx);printf("\n");
			lastans=ans%last;
		}
	}
	return 0;
}

忠告:做这题的时候,佛系一点总不会错(为什么放最后呀。。)

posted @ 2021-07-26 16:08  Illusory_dimes  阅读(126)  评论(0编辑  收藏  举报