Loading

P4119 [Ynoi2018] 未来日记

\(\text{Links}\)

LuoguBlog

P4119 [Ynoi2018] 未来日记

题外话

  • 个人生涯中第一道独立通过的 Ynoi 大分块!!

  • 同时也是个人生涯中通过的第十道 Ynoi 系列题目!!

  • 卡了好久结果加了个优化就过了/yun

  • 感觉 \(8.5\) 的评分还是有点虚高(,个人体感 \(7\)


题意

  • 将区间内的所有 \(x\) 变成 \(y\)

  • 查询区间第 \(k\)

\(1\le n,m,a_i\le 10^5\)


题解

首先考虑静态区间第 \(k\) 小怎么做。

可以二分答案,然后查询区间内 \(\le x\) 的数量,\(O(n\sqrt n\log n)\)

套个值域分块,分别记录序列上前 \(i\) 个块中 \(j\) 的出现次数和第 \(j\) 个值域块中的值的出现次数之和。查询时先把散块的信息处理出来,再扫值域块 \(O(1)\) 累加次数,确定答案在哪个值域块,然后进入答案块中继续 \(O(1)\) 累加次数,直到加到 \(\ge k\) 就找到了答案。

然后考虑修改,维护上面提到的那些信息都很简单,把之前的 \(cnt_x\) 求出来,然后往后暴力更新就好了。

但是散块的查询就会比较难整,那么考虑怎样查询每个位置的值。

结合上修改操作,再加上第二分块赋予的经验,不难想到用并查集维护,在每个块内把相同颜色的位置并在一起,修改的时候整块直接把 \(x\) 并给 \(y\),散块暴力重构就好了。查询散块的时候直接 \(\text{find}\) 每个位置的值就好。

\(O(n\alpha(n)\sqrt n)\)


进入正题 卡常

  • 块长稍微开大一点吧,大概不低于 \(300\),本人 AC 的时候取的是 \(400\)

  • 修改操作暴力重构散块的时候,忽略除了 \(x\)\(y\) 以外的值!本人就是靠这个优化 AC 的!

  • 同块内的查询直接 \(\text{nth-element}\)

  • \(\text{fread + cout}\)


\(\texttt{Code}\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define il inline
#define re register
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
char buf[1<<22],*p1=buf,*p2=buf;
const int N=1e5+5,sq=255,V=1e5,len=400;
int n,m,a[N],id[N],L[sq],R[sq],bl[sq],br[sq];
int fa[N],rt[sq][N],col[N],s[sq][sq],cnt[sq][N];
int cntx[sq],t[N],tb[sq];
il int read(){
    re int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}
il void init(){
    re int i;
    for(i=1;i<=V;i++)id[i]=(i-1)/len+1;
    for(i=1;i<=id[V];i++)
        L[i]=bl[i]=R[i-1]+1,R[i]=br[i]=i*len;
    R[id[n]]=n,br[id[V]]=V;
}
il void Build(int i){
    re int j;
    for(j=L[i];j<=R[i];j++){
        cnt[i][a[j]]++,s[i][id[a[j]]]++;
        if(rt[i][a[j]])fa[j]=rt[i][a[j]];
        else fa[j]=rt[i][a[j]]=j,col[j]=a[j];
    }
    for(j=1;j<=id[V];j++)s[i][j]+=s[i-1][j];
    for(j=1;j<=V;j++)cnt[i][j]+=cnt[i-1][j];
}
il int fnd(int x){
    return (x==fa[x])?x:(fa[x]=fnd(fa[x]));
}
il void ReBuild(int l,int r,int x,int y){
    re int i;
    cntx[id[l]]=0;
    for(i=L[id[l]];i<=R[id[l]];i++)a[i]=col[fnd(i)];
    for(i=l;i<=r;i++)cntx[id[l]]+=(a[i]==x);
    for(i=L[id[l]];i<=R[id[l]];i++)
        if(a[i]==x||a[i]==y)rt[id[l]][a[i]]=0;
    if(cntx[id[l]]){
        cntx[id[l]]=0;
        for(i=l;i<=r;i++)
            if(a[i]==x)cntx[id[l]]++,a[i]=y;
    }
    for(i=L[id[l]];i<=R[id[l]];i++)if(a[i]==x||a[i]==y){
        if(rt[id[l]][a[i]])fa[i]=rt[id[l]][a[i]];
        else rt[id[l]][a[i]]=fa[i]=i,col[i]=a[i];
    }
}
il void upd(int st,int ed,int x,int y){
    re int tot=0,i;
    for(i=st;i<=ed;i++){
        tot+=cntx[i];
        cnt[i][x]-=tot,cnt[i][y]+=tot,
        s[i][id[x]]-=tot,s[i][id[y]]+=tot;
    }
    for(i=ed+1;i<=id[n];i++)
        cnt[i][x]-=tot,cnt[i][y]+=tot,
        s[i][id[x]]-=tot,s[i][id[y]]+=tot;
}
il void Modify(int l,int r,int x,int y){
    if(x==y)return;
    if(id[l]==id[r])return ReBuild(l,r,x,y),upd(id[l],id[r],x,y);
    re int i;
    for(i=id[l]+1;i<id[r];i++){
        cntx[i]=cnt[i][x]-cnt[i-1][x];
        if(rt[i][x]){
            if(rt[i][y])fa[rt[i][x]]=rt[i][y],rt[i][x]=0;
            else rt[i][y]=rt[i][x],col[rt[i][y]]=y,rt[i][x]=0;
        }
    }
    ReBuild(l,R[id[l]],x,y),ReBuild(L[id[r]],r,x,y);
    upd(id[l],id[r],x,y);
}
il void Solve(int l,int r,int k){
    re int i,j,st=id[l],ed=id[r]-1,dt,tot=0,tmp;
    if(id[l]==id[r]){
        for(i=l;i<=r;i++)a[i]=col[fnd(i)];
        nth_element(a+l,a+l+k-1,a+r+1);
        return cout<<a[l+k-1]<<'\n',void();
    }
    for(i=1;i<=id[V];i++)tb[i]=0;
    for(i=l;i<=R[id[l]];i++)
        t[a[i]=col[fnd(i)]]=0,tb[id[a[i]]]++;
    for(i=L[id[r]];i<=r;i++)
        t[a[i]=col[fnd(i)]]=0,tb[id[a[i]]]++;
    for(i=1;i<=id[V];i++){
        dt=tb[i]+s[ed][i]-s[st][i];
        if(tot+dt>=k){
            for(j=bl[i];j<=br[i];j++)t[j]=0;
            for(j=l;j<=R[id[l]];j++)t[a[j]]++;
            for(j=L[id[r]];j<=r;j++)t[a[j]]++;
            for(j=bl[i];j<=br[i];j++){
                tmp=t[j]+cnt[ed][j]-cnt[st][j];
                if(tot+tmp>=k)
                    return cout<<j<<'\n',void();
                else tot+=tmp;
            }
        }
        else tot+=dt;
    }
}
int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    n=read(),m=read();
    re int i;
    for(i=1;i<=n;i++)a[i]=read();
    init();
    for(i=1;i<=id[n];i++)Build(i);
    while(m--){
        re int op=read(),l=read(),r=read(),x=read();
        if(op==1)Modify(l,r,x,read());
        else Solve(l,r,x);
    }
    return 0;
}
posted @ 2023-12-07 17:26  MrcFrst  阅读(38)  评论(0编辑  收藏  举报