luogu P4117 [Ynoi2018] 五彩斑斓的世界 题解
这是九大分块中最简单的一道题目,题面如下。
二阶堂真红给了你一个长为 \(n\) 的序列 \(a\),有 \(m\) 次操作
- 把区间 \([l,r]\) 中大于 \(x\) 的数减去 \(x\)。
- 查询区间 \([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;
}
浙公网安备 33010602011771号