luogu P4117 [Ynoi2018] 五彩斑斓的世界 题解

这是九大分块中最简单的一道题目,题面如下。

二阶堂真红给了你一个长为 \(n\) 的序列 \(a\),有 \(m\) 次操作

  1. 把区间 \([l,r]\) 中大于 \(x\) 的数减去 \(x\)
  2. 查询区间 \([l,r]\)\(x\) 的出现次数。

对于 \(100\%\) 的数据,\(1\le n\le 10^6\)\(1\le m\le 5\times 10^5\)\(1\le l\le r \le n\)\(0 \le a_i,x \le 10^5+1\)

64MB 7.5s

显然可以使用分块解决,我们考虑对于每个散块直接暴力重构,考虑到值域的范围其实很小,而且最大值不会增加,我们对值域进行并查集连边处理,但有一个问题,就是如果说存在大于\(2*x\)的数,那么直接用并查集连边可能会导致与减去\(2*x\),那么考虑另外一个经典trick,我们可以打一个标记,让所有数加上\(x\),然后让小于\(x\)的数全部加上\(x\)就可以,因为空间限制极小,我们可以离线下来然后挨个块处理,同一时间只处理一个块内的信息即可。

代码如下:

#include <bits/stdc++.h>
using namespace std;
int n,m,len,tp,st[300010],a[1000010],fa[100010],size[100010],mx,tag,ll,rr,ans[500010],lls[1000010];
struct edge{
	int l;
	int r;
	int op;
	int val;
}wen[500010];
inline void merge(int q,int w){
	st[++tp]=w;
	fa[q]=w;
	size[w]+=size[q];
	size[q]=0;
	return;
}
inline int find(int q){
	if(fa[q]==q){
		return q;
	}
	fa[q]=find(fa[q]);
	return fa[q];
}
inline void modify(int val){
	if(tag+val*2<=mx){
		for(int i=tag+1;i<=tag+val;i++){
			if(size[i]){
				merge(i,i+val);
			}
		}
		tag+=val;
	}
	else{
		for(int i=mx;i>=tag+val+1;i--){
			if(size[i]){
				merge(i,i-val);
			}
		}
		if(tag+val<mx){
			mx=tag+val;
		}
	}
	return;
}
inline void clears(){
	for(int i=1;i<=tp;i++){
		fa[st[i]]=st[i];
		size[st[i]]=0;
	}
	tp=0;
	return;
}
inline void build(int l,int r){
	clears();
	mx=0;
	tag=0;
	for(int i=l;i<=r;i++){
		mx=max(mx,a[i]);
		size[a[i]]++;
		st[++tp]=a[i];
	}
	return;
}
inline void san(int l,int r,int ying){
	for(int i=l;i<=r;i++){
		lls[i]=find(a[i]);
	}
	for(int i=l;i<=r;i++){
		if(a[i]!=0){
			a[i]=lls[i];
			a[i]-=tag;
		}
	}
	ll=max(l,wen[ying].l);
	rr=min(r,wen[ying].r);
	for(int i=ll;i<=rr;i++){
		if(a[i]>wen[ying].val){
			a[i]-=wen[ying].val;
		}
	}
	build(l,r);
	return;
}
inline void query(int l,int r,int ying){
	if(wen[ying].val+tag>1e5+1){
		return;
	}
	if(wen[ying].val==0){
		if(wen[ying].l<=l&&r<=wen[ying].r){
			ans[ying]+=size[0];
			return;
		}
		ll=max(l,wen[ying].l);
		rr=min(r,wen[ying].r);
		for(int i=ll;i<=rr;i++){
			if(a[i]==0){
				ans[ying]++;
			}
		}
		return;
	}
	if(wen[ying].l<=l&&r<=wen[ying].r){
		ans[ying]+=size[wen[ying].val+tag];
	}
	else{
		ll=max(l,wen[ying].l);
		rr=min(r,wen[ying].r);
		for(int i=ll;i<=rr;i++){
			if(find(a[i])-tag==wen[ying].val){
				ans[ying]++;
			}
		}
	}
	return;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		cin>>wen[i].op>>wen[i].l>>wen[i].r>>wen[i].val;
	}
	len=sqrt(n);
	for(int i=0;i<=1e5+1;i++){
		fa[i]=i;
	}
	for(int l=1;l<=n;l+=len){
		int r=l+len-1;
		build(l,r);
		for(int j=1;j<=m;j++){
			if(wen[j].r<l||wen[j].l>r){
				continue;
			}
			else if(wen[j].op==1){
				if(wen[j].l<=l&&r<=wen[j].r){
					modify(wen[j].val);
				}
				else{
					san(l,r,j);
				}
			}
			else{
				query(l,r,j);
			}
		}
	}
	for(int i=1;i<=m;i++){
		if(wen[i].op==2){
			cout<<ans[i]<<'\n';
		}
	}
	return 0;
}
posted @ 2025-04-20 18:01  特别之处  阅读(23)  评论(0)    收藏  举报