[Ynoi2018] 五彩斑斓的世界

[Ynoi2018] 五彩斑斓的世界

突刺贯穿的第二分块。

题意

给定长度为 \(n\) 的序列 \(a\),有 \(m\) 次操作,每次操作有两种形式:

  • 将区间 \([l,r]\) 中大于 \(x\) 的数减去 \(x\)

  • 求区间 \([l,r]\) 中等于 \(x\) 的数的个数。

\(1 \leq n \leq 10^6\)\(1 \leq m \leq 5 \times 10^5\)\(0 \leq a_i,x \leq 10^5+1\)

思路

看到 Ynoi 题可以直接考虑分块了。

考虑整块的修改操作。假设块中元素上界为 \(mx\),则有:

  • \(mx < 2 \times x\),此时我们对值域在 \([x+1,mx]\) 中的元素减去 \(x\),然后令 \(mx\) 改为 \(x\)

  • \(mx \geq 2 \times x\),此时我们对值域在 \([0,x]\) 的元素加上 \(x\),再打一个整块减 \(x\) 的标记,然后令 \(mx\) 减去 \(x\)

虽然 \(mx\) 不是严格的上界,但我们仍然可以证明,单个块产生的修改总数是不超过极差也就是 \(O(V)\) 的。

现在考虑如何快速维护这一信息,发现对每个值开一个并查集即可。

但是我们发现这题卡了空间。块与块之间互不影响,所以我们离线下来逐块处理。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int V=100001;
int n,m,num[1000010],ans[500010],root[100010],rev[1200010],cnt[1200010],p[1200010];
struct Query{
	int cmd,l,r,x;
}query[500010];
int Find(int pos){
	if(pos!=p[pos]) p[pos]=Find(p[pos]);
	return p[pos];
}
void calc(int L,int R){
	int mx=V,tag=0,tot=n;
	for(int i=0;i<=V;i++){
		tot++;
		root[i]=tot;
		rev[tot]=i;
		p[root[i]]=root[i];
		cnt[root[i]]=0;
	}
	for(int i=L;i<=R;i++){
		p[i]=root[num[i]];
		cnt[root[num[i]]]++;
	}
	for(int i=1;i<=m;i++){
		int cmd=query[i].cmd,l=max(query[i].l,L),r=min(query[i].r,R),x=query[i].x;
		if(l>r){
			continue;
		}
		if(cmd==1  &&  x){
			if(l==L  &&  r==R){
			    if(mx<=x){
			        continue;
			    }
				else if(mx<2*x){
					for(int j=x+1+tag;j<=mx+tag;j++){
						p[root[j]]=root[j-x];
						cnt[root[j-x]]+=cnt[root[j]];
						cnt[root[j]]=0;
						tot++;
						root[j]=tot;
						rev[tot]=j;
						p[root[j]]=root[j];
						cnt[root[j]]=0;
					}
					mx=x;
				}
				else{
					for(int j=x+tag;j>=tag;j--){
						p[root[j]]=root[j+x];
						cnt[root[j+x]]+=cnt[root[j]];
						cnt[root[j]]=0;
						tot++;
						root[j]=tot;
						rev[tot]=j;
						p[root[j]]=root[j];
						cnt[root[j]]=0;
					}
					tag+=x;
					mx-=x;
				}
			}
			else{
				for(int j=l;j<=r;j++){
					int num=rev[Find(j)];
					if(num-tag>x){
						cnt[root[num]]--;
						cnt[root[num-x]]++;
						p[j]=root[num-x];
					}
				}
			}
		}
		if(cmd==2){
			if(l==L  &&  r==R){
				if(x+tag<=V) ans[i]+=cnt[root[x+tag]];
			}
			else{
				for(int j=l;j<=r;j++){
					int num=rev[Find(j)]-tag;
					if(num==x){
						ans[i]++;
					}
				}
			}
		}
	}
}
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&num[i]);
	}
	for(int i=1;i<=m;i++){
		scanf("%d %d %d %d",&query[i].cmd,&query[i].l,&query[i].r,&query[i].x);
	}
	int block=round(sqrt(n));
	for(int L=1;L<=n;L++){
		int R=min(L+block-1,n);
		calc(L,R);
		L=R;
	}
	for(int i=1;i<=m;i++){
		if(query[i].cmd==2){
			printf("%d\n",ans[i]);
		}
	}
	return 0;
}
posted @ 2025-06-03 13:33  Oken喵~  阅读(2)  评论(0)    收藏  举报