洛谷P4117[Ynoi2018]五彩斑斓的世界(分块)

为什么你们都用并查集啊qwq

首先,可以发现每一块的状态都是独立的,所以可以逐块操作,就可以做到 $O(n+m)$ 空间。

对于每一块,对每个 $a_i$ 开一个链表,记录值为 $a_i$ 的数的位置。对于块内修改,把 $a_i$ 还原后暴力重构;对于全局修改,考虑记录 $a_i$ 的最大值 $maxn$,若 $2x\ge maxn$,则把 $[x+1,maxn]$ 上的链表合并到 $[1,maxn-x]$ 上;若 $2x<maxn$,则把 $[1,x]$ 合并到 $[x+1,2x]$上,全局减 $x$。

不难发现每次合并都意味着合法值域变小,因此每块的合并次数不会超过值域大小。总时间复杂度是 $O((n+m)\sqrt{n})$。

对于查询,整块的直接输出相应的链表大小,块内的把相应链表扫描一遍即可,不用重构。

code:

#include<cstdio>
#define For(i,A,B) for(i=(A);i<=(B);++i)
const int N=500050;
const short S=750;
const int BUF=1<<23;
char rB[BUF],*rS,*rT,wB[BUF+50],*wT=wB;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,BUF,stdin),rS==rT)?EOF:*rS++;}
inline void flush(){fwrite(wB,1,wT-wB,stdout);wT=wB;}
inline int rd(){
    char c=gc();
    while(c<48||c>57)c=gc();
    int x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=x*10+(c&15);
    return x;
}
short buf[15];
inline void wt(int x){
    short l=-1;
    if(wT-wB>BUF)flush();
    while(x>9){
        buf[++l]=x%10;
        x/=10;
    }
    *wT++=x|48;
    while(l>=0)*wT++=buf[l--]|48;
    *wT++='\n';
}
int a[N],opt[N],ql[N],qr[N],qx[N],ps[S+5],st[N+S],sl=-1,s[N],mxn,decv;
//st 用于记录历史上的非空链表(也就是重构时要清空的链表),它的大小和插入与合并的总次数等规模
//注意 mxn 保存的是真实的最大值
short nxt[S+5],bg[N],ed[N],cnt[N];
inline int Min(int a,int b){return a<b?a:b;}
inline int Max(int a,int b){return a>b?a:b;}
inline short bel(int x){return (x-1)/S+1;}
inline void ins(int x,short p){
    if(!bg[x]){
        st[++sl]=x;
        bg[x]=ed[x]=p;
        cnt[x]=1;
    }else{
        nxt[ed[x]]=p;
        ed[x]=p;
        ++cnt[x];
    }
}
inline void merg(int x,int y){
    if(!bg[x]){
        st[++sl]=x;
        bg[x]=bg[y];ed[x]=ed[y];
        cnt[x]=cnt[y];
    }else{
        nxt[ed[x]]=bg[y];
        ed[x]=ed[y];
        cnt[x]+=cnt[y];
    }
    bg[y]=ed[y]=cnt[y]=0;
}
inline void clr(int x){
    for(short i=bg[x],t=nxt[i];i;t=nxt[i=t]){
        a[ps[i]]=x-decv;
        nxt[i]=0;
    }
    bg[x]=ed[x]=cnt[x]=0;
}
inline void build(int L,int R){  //重构
    int i;
    mxn=0;
    For(i,L,R){
        ins(a[i],i-L+1);
        mxn=Max(mxn,a[i]);
    }
}
inline void back(){  //拆链表
    while(sl>=0){clr(st[sl--]);}
    decv=0;
}
int main(){
    int n=rd(),m=rd(),q=0,i,j,L,R;
    short k,t,tot=bel(n);
    For(i,1,n)a[i]=rd();
    For(i,1,m){
        opt[i]=rd();ql[i]=rd();qr[i]=rd();qx[i]=rd();
        opt[i]=opt[i]==1?0:++q;
    }
    For(k,1,tot){
        L=(int)(k-1)*S+1;R=Min((int)k*S,n);
        For(i,L,R)ps[i-L+1]=i;
        build(L,R);
        For(i,1,m){
            if(ql[i]>R||qr[i]<L||mxn<qx[i])continue;
            if(opt[i])if(bel(ql[i])==k||bel(qr[i])==k)for(t=bg[qx[i]+decv];t;t=nxt[t])if(ps[t]>=ql[i]&&ps[t]<=qr[i])++s[opt[i]];  //块内查询
            else;
            else s[opt[i]]+=cnt[qx[i]+decv];  //整块查询
            else if(bel(ql[i])==k||bel(qr[i])==k){  //块内修改
                back();
                For(j,Max(L,ql[i]),Min(R,qr[i]))if(a[j]>qx[i])a[j]-=qx[i];
                build(L,R);
            }else if(mxn<=qx[i]*2){
                For(j,qx[i]+decv+1,mxn+decv)if(bg[j])merg(j-qx[i],j);
                mxn=qx[i];  //其实 mxn 不一定是 qx[i],但是只要它和值域一起缩小就不会影响复杂度
            }else{
                For(j,1+decv,qx[i]+decv)if(bg[j])merg(j+qx[i],j);
                decv+=qx[i];
                mxn-=qx[i];
            }
//整块修改,两类情况如上所述
        }
        back();
    }
    For(i,1,q)wt(s[i]);
    flush();
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/sunshine-chen/p/12677883.html