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

突刺贯穿第二分块

题意:要求对长度为 \(n\),值域为 \(V\) 的序列执行 \(m\) 次操作。操作一为将区间中大于 \(x\) 的数减去 \(x\),操作二为询问区间内 \(x\) 的出现次数。\(n\le 10^6,V\le 10^5,m\le 5\times 10^5\)。

我们可以用一个并查集把相同的数都划分到同一个集合里,当发生减法的时候就合并集合。操作一若是直接减的话,要对值域里大于 \(x\) 的数都判断一次其是否在当前块中出现,当 \(x\) 远小于区间最值的时候会超出时限。设当前块的最值为 \(mx\),则我们考虑平衡一下操作一,当 \(x\) 接近 \(mx\) 的时候,我们把 \([x+1,mx]\) 中所有出现过的数都和其减去 \(x\) 的集合合并,当 \(x\) 较小时,把 \([0,x]\) 中的数向加上 \(x\) 的集合合并,再标记全局减 \(x\)。这样每次操作的数都是更少的。

对于散块修改则暴力操作后,重新建立新的并查集。

由于空间限制严格,我们必须依次对每个块执行所有操作。

#include<bits/stdc++.h>
using namespace std;
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int read(){
    int k=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())k=(k*10+c-'0');
    return k*f;
}
void write(int x){
    if(x<10)putchar(x+'0');
    else write(x/10),putchar(x%10+'0');
}
int n,m,t;
const int N = 1e5+10;
int a[N*10],ans[5*N];
int op[5*N],ql[5*N],qr[5*N],qx[5*N];
int fa[10*N],val[10*N],rt[N],cnt[N],mx,tag;
int find(int x) {
    while(x!=fa[x])x=fa[x]=fa[fa[x]];
    return x;
}
void merge(int x,int y){
    if(!rt[y]){
        rt[y]=rt[x];
        val[rt[y]]=y;
    }else{
        fa[rt[x]]=rt[y];
    }
    cnt[y]+=cnt[x];
    cnt[x]=0;rt[x]=0;
}
void solve(int l,int r){
    mx=tag=0;
    for(int i=l;i<=r;i++){
        mx=max(a[i],mx);
        if(!rt[a[i]]){
            rt[a[i]]=fa[i]=i;
            val[i]=a[i];
        }else{
            fa[i]=rt[a[i]];
        }
        cnt[a[i]]++;
    }
    for(int i=1;i<=m;i++){
        if(ql[i]>r||qr[i]<l)continue;
        int x=qx[i];
        if(ql[i]<=l&&r<=qr[i]){
            if(op[i]==1){
                if(!x||mx<=x)continue;
                if(2*x>=mx-tag){
                    for(int v=x+tag+1;v<=mx;v++){
                        if(rt[v])merge(v,v-x);
                    }
                    mx=min(mx,x+tag);
                }else{
                    for(int v=x+tag;v>=tag;v--){
                        if(rt[v])merge(v,v+x);
                    }
                    tag+=x;
                }
            }else{
                if(x+tag<N)ans[i]+=cnt[x+tag];
            }
        }else{
            if(op[i]==1){
                if(!x||mx<=x)continue;
                for(int i=l;i<=r;i++){
                    a[i]=val[find(i)];
                }
                for(int i=l;i<=r;i++){
                    rt[a[i]]=cnt[a[i]]=0;
                    val[i]=0;fa[i]=0;
                    a[i]-=tag;
                }
                for(int j=max(l,ql[i]);j<=min(r,qr[i]);j++){
                    if(a[j]>x)a[j]-=x;
                }
                mx=tag=0;
                for(int i=l;i<=r;i++){
                    mx=max(a[i],mx);
                    if(!rt[a[i]]){
                        rt[a[i]]=fa[i]=i;
                        val[i]=a[i];
                    }else{
                        fa[i]=rt[a[i]];
                    }
                    cnt[a[i]]++;
                }
            }else{
                for(int j=max(l,ql[i]);j<=min(r,qr[i]);j++){
                    if(x+tag<N)ans[i]+=(val[find(j)]-tag==x);
                }
            }
        }
    }
    for(int i=l;i<=r;i++){
        a[i]=val[find(i)];
        rt[a[i]]=cnt[a[i]]=0;
    }
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    n=read(),m=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
    }
    for(int i=1;i<=m;i++){
        op[i]=read(),ql[i]=read(),qr[i]=read(),qx[i]=read();
    }
    t=666;
    for(int i=1;i<=n;i+=t){
        solve(i,min(i+t-1,n));
    }
    for(int i=1;i<=m;i++){
        if(op[i]==2){
            write(ans[i]);
            putchar(endl);
        }
    }
    return 0;
}
posted @ 2026-09-26 14:18  farenara_sayowell  阅读(2)  评论(0)    收藏  举报