[Ynoi2018]未来日记

分块神题。

看了一会儿题解,看懂了思路,然后写了两个小时,调了一个多小时,好多地方写错了。

我们考虑对序列和值域都分块。\(sum1[i][j]\) 表示前 \(i\) 个块,第 \(j\) 块值域有几个数,\(sum2[i][j]\) 表示前 \(i\) 个块,值为 \(j\) 有几个数,\(id[i][j]\) 表示第 \(i\) 块值为 \(j\) 的编号,\(mp[i][j]\) 表示第 \(i\) 块编号为 \(j\) 值为多少,这样空间正好开的下。

对于修改操作,我们考虑边角暴力。在整块中若只有 \(x\) 那么直接修改,若又有 \(y\) 的话就暴力重构。每次重构整块中不同的数的数目会 \(-1\),一个块最多被重构 \(\sqrt{n}\) 块,所以时间复杂度保证为 \(O(n\sqrt{n})\)

对于询问操作,我们利用 \(sum1\)\(sum2\) 可以在 \(O(\sqrt{n})\) 得到。

时间复杂度 \(O(n\sqrt{n})\),空间复杂度 \(O(n\sqrt{n})\)

洛谷加了 \(fread,fwrite\) 才卡过去,空间差点 \(MLE\)。。。

\(Code\ Below:\)

#include <bits/stdc++.h>
using namespace std;
const int maxn=100000+10;
const int maxblo=317;
const int lim=100000;
int n,m,blo,a[maxn],pos[maxn],bl[maxn],L[maxn],R[maxn];
int v1[maxn],v2[maxn],sum1[maxblo][maxblo],sum2[maxblo][maxn],id[maxblo][maxn],mp[maxblo][maxblo];

namespace FastIO{
    #define gc() (iS==iT?(iT=(iS=ibuff)+fread(ibuff,1,SIZ,stdin),(iS==iT?EOF:*iS++)):*iS++)
    const int SIZ=1<<21|1;
    char *iS,*iT,ibuff[SIZ],obuff[SIZ],*oS=obuff,*oT=oS+SIZ-1,fu[110],c;int fr;
    inline void out(){
        fwrite(obuff,1,oS-obuff,stdout);
        oS=obuff;
    }
    template <class T>
    inline void read(T &x){
        x=0;T y=1;
        for(c=gc();(c>'9'||c<'0')&&c!='-';c=gc());
        c=='-'?y=-1:x=(c&15);
        for(c=gc();c>='0'&&c<='9';c=gc()) x=x*10+(c&15);
        x*=y;
    }
    template <class T>
    inline void print(T x,char text='\n'){
        if(x<0) *oS++='-',x*=-1;
        if(x==0) *oS++='0';
        while(x) fu[++fr]=x%10+'0',x/=10;
        while(fr) *oS++=fu[fr--];
        *oS++=text;out();
    }
}
using namespace FastIO;

inline void reset(int x){
    for(int i=L[x];i<=R[x];i++) a[i]=mp[x][pos[i]];
}

inline void change(int bel,int x,int y){
    int last=id[bel][x];id[bel][y]=last;
    mp[bel][last]=y;id[bel][x]=0;
}

inline void build(int x){
    for(int i=1;i<=blo;i++) id[x][mp[x][i]]=0;
    int ind=0;
    for(int i=L[x];i<=R[x];i++)
        if(!id[x][a[i]]) id[x][a[i]]=++ind,mp[x][ind]=a[i];
    for(int i=L[x];i<=R[x];i++) pos[i]=id[x][a[i]];
}

inline void rebuild(int l,int x,int y){
    for(int i=bl[l];i<=bl[n];i++){
        sum1[i][bl[x]]+=sum1[i-1][bl[x]];
        sum1[i][bl[y]]+=sum1[i-1][bl[y]];
        sum2[i][x]+=sum2[i-1][x];
        sum2[i][y]+=sum2[i-1][y];
    }
}

inline void modify(int l,int r,int x,int y){
    if(sum2[bl[r]][x]==sum2[bl[l]-1][x]) return ;
    for(int i=bl[n];i>=bl[l];i--){
        sum1[i][bl[x]]-=sum1[i-1][bl[x]];
        sum1[i][bl[y]]-=sum1[i-1][bl[y]];
        sum2[i][x]-=sum2[i-1][x];
        sum2[i][y]-=sum2[i-1][y];
    }
    if(bl[l]==bl[r]){
        reset(bl[l]);
        for(int i=l;i<=r;i++)
            if(a[i]==x){
                a[i]=y;
                sum1[bl[l]][bl[x]]--;
                sum1[bl[l]][bl[y]]++;
                sum2[bl[l]][x]--;
                sum2[bl[l]][y]++;
            }
        build(bl[l]);rebuild(l,x,y);
        return ;
    }
    reset(bl[l]);reset(bl[r]);
    for(int i=l;i<=R[bl[l]];i++)
        if(a[i]==x){
            a[i]=y;
            sum1[bl[l]][bl[x]]--;
            sum1[bl[l]][bl[y]]++;
            sum2[bl[l]][x]--;
            sum2[bl[l]][y]++;
        }
    for(int i=L[bl[r]];i<=r;i++)
        if(a[i]==x){
            a[i]=y;
            sum1[bl[r]][bl[x]]--;
            sum1[bl[r]][bl[y]]++;
            sum2[bl[r]][x]--;
            sum2[bl[r]][y]++;
        }
    build(bl[l]);build(bl[r]);
    for(int i=bl[l]+1;i<bl[r];i++){
        if(!sum2[i][x]) continue;
        if(sum2[i][y]){
            reset(i);
            for(int j=L[i];j<=R[i];j++)
                if(a[j]==x){
                    a[j]=y;
                    sum1[i][bl[x]]--;
                    sum1[i][bl[y]]++;
                    sum2[i][x]--;
                    sum2[i][y]++;
                }
            build(i);
        }
        else {
            sum1[i][bl[x]]-=sum2[i][x];
            sum1[i][bl[y]]+=sum2[i][x];
            sum2[i][y]+=sum2[i][x];
            sum2[i][x]=0;
            change(i,x,y);
        }
    }
    rebuild(l,x,y);
}

inline int query(int l,int r,int k){
    if(bl[l]==bl[r]){
        reset(bl[l]);
        for(int i=l;i<=r;i++) v1[i]=a[i];
        nth_element(v1+l,v1+l+k-1,v1+r+1);
        int ans=v1[l+k-1];
        for(int i=l;i<=r;i++) v1[i]=0;
        return ans;
    }
    reset(bl[l]);reset(bl[r]);
    for(int i=l;i<=R[bl[l]];i++) v1[bl[a[i]]]++,v2[a[i]]++;
    for(int i=L[bl[r]];i<=r;i++) v1[bl[a[i]]]++,v2[a[i]]++;
    for(int i=1;i<=bl[lim];i++){
        if(k>v1[i]+sum1[bl[r]-1][i]-sum1[bl[l]][i]) k-=v1[i]+sum1[bl[r]-1][i]-sum1[bl[l]][i];
        else {
            for(int j=(i-1)*blo+1;j<=i*blo;j++){
                if(k>v2[j]+sum2[bl[r]-1][j]-sum2[bl[l]][j]) k-=v2[j]+sum2[bl[r]-1][j]-sum2[bl[l]][j];
                else {
                    for(int x=l;x<=R[bl[l]];x++) v1[bl[a[x]]]--,v2[a[x]]--;
                    for(int x=L[bl[r]];x<=r;x++) v1[bl[a[x]]]--,v2[a[x]]--;
                    return j;
                }
            }
        }
    }
}

int main()
{
    read(n),read(m);blo=sqrt(lim)+1;
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=lim;i++){
        bl[i]=(i-1)/blo+1;
        if(bl[i]!=bl[i-1]) L[bl[i]]=i,R[bl[i-1]]=i-1;
    }
    R[bl[n]]=n;
    for(int i=1;i<=bl[n];i++) build(i);
    for(int x=1;x<=bl[n];x++){
        for(int i=1;i<=bl[lim];i++) sum1[x][i]=sum1[x-1][i];
        for(int i=1;i<=lim;i++) sum2[x][i]=sum2[x-1][i];
        for(int i=L[x];i<=R[x];i++) sum1[x][bl[a[i]]]++,sum2[x][a[i]]++;
    }
    int op,l,r,x,y;
    while(m--){
        read(op),read(l),read(r),read(x);
        if(op==1) read(y),modify(l,r,x,y);
        else print(query(l,r,x));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/owencodeisking/p/10324780.html
今日推荐