[Ynoi2018]未来日记
Posted zzrblogs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Ynoi2018]未来日记相关的知识,希望对你有一定的参考价值。
"望月悲叹的最初分块" (妈呀这名字好中二啊(谁叫我要用日本轻小说中的东西命名真是作死))
这里就直接挂csy的题解了,和我的不太一样,但是大概思路还是差不多的,我的做法是和“五彩斑斓的世界”有点类似的维护方法
先考虑如何求区间第k小值。对序列和权值都进行分块,设bi,j表示前j 块中权值在i 块内的数字个数,ci,j 表示前j 块中数字i 的出现次数。那么对于一个询问[l,r] ,首先将零碎部分的贡献加入到临时数组tb 和tc 中,然后枚举答案位于哪一块,确定位于哪一块之后再暴力枚举答案即可在O(√n) 的时间内求出区间第k小值。
接着考虑如何实现区间[l,r]内x 变成y 的功能。显然对于零碎的两块,可以直接暴力重构整块。对于中间的每个整块,如果某一块不含x ,那么无视这一块;否则如果这一块不含y ,那么只需要将x 映射成y ;否则这一块既有x 又有y ,这意味着x 与y 之间发生了合并,不妨直接暴力重构整块。因为有c 数组,我们可以在O(1) 的时间内知道某一块是否有某个数。
考虑什么情况下会发生重构,也就是一个块内发生了一次合并的时候。一开始长度为nn 的序列会提供O(n) 次合并的机会,而每次修改会对零碎的两块各提供一次机会,故总合并次数不超过O(n+m) ,因此当发生合并时直接重构并不会影响复杂度。
那么现在每块中的转换情况只可能是一条条互不相交的链,只需要记录每个初值转换后是什么,以及每个现值对应哪个初值即可。遇到查询的时候,我们需要知道零碎部分每个位置的值,不妨直接重构那两块,然后遍历一遍原数组a即可得到每个位置的值。
在修改的时候,还需要同步维护b 和c 数组,因为只涉及两个权值,因此暴力修改j 这一维也是可以承受的。
总时间复杂度O((n+m)√n) ,空间复杂度O(n√n)
// luogu-judger-enable-o2 #include<bits/stdc++.h> using namespace std; const int N=1e5+5,BLO=370; int n,m,a[N],p,vec[N],v2[BLO]; int bl[N],L[BLO],R[BLO]; int s1[BLO][BLO],s2[BLO][N],id[BLO][N],rid[BLO][BLO],pos[N]; int sta[20],ttop=0,lp=0; char pr[700005]; template <typename T> inline void read(T &x) { x=0;register char flag,c=getchar();while(c<‘0‘||c>‘9‘) flag=c,c=getchar(); while(c>=‘0‘&&c<=‘9‘) x=x*10+(c^48),c=getchar(); if(flag==‘-‘) x=-x; } template <typename T> inline void print(T x) { ttop=0; do { sta[++ttop]=(int)(x%10),x/=10; }while(x); while(ttop) pr[lp++]=sta[ttop--]+‘0‘; pr[lp++]=10; } inline void reset(int x) { for(register int i=L[x];i<=R[x];++i) a[i]=rid[x][pos[i]]; } inline void change(int bel,int x,int y) { int uid=id[bel][x];id[bel][y]=uid,rid[bel][uid]=y,id[bel][x]=0; } inline void build(int x) { for(register int i=1;i<=p;++i) id[x][rid[x][i]]=0; for(register int i=L[x],idx=0;i<=R[x];++i) if(!id[x][a[i]]) id[x][a[i]]=++idx,rid[x][idx]=a[i]; for(register int i=L[x];i<=R[x];++i) pos[i]=id[x][a[i]]; } inline void rebuild(int l,int x,int y) { for(register int i=bl[l];i<=bl[n];++i) { s2[i][x]+=s2[i-1][x],s2[i][y]+=s2[i-1][y]; s1[i][bl[x]]+=s1[i-1][bl[x]],s1[i][bl[y]]+=s1[i-1][bl[y]]; } } inline void modify(int l,int r,int x,int y) { if(s2[bl[r]][x]-s2[bl[l]-1][x]==0) return ; for(int i=bl[n];i>=bl[l];--i) { s2[i][x]-=s2[i-1][x],s2[i][y]-=s2[i-1][y]; s1[i][bl[x]]-=s1[i-1][bl[x]],s1[i][bl[y]]-=s1[i-1][bl[y]]; } if(bl[l]==bl[r]) { reset(bl[l]); for(int i=l;i<=r;++i) if(a[i]==x) { a[i]=y; --s2[bl[l]][x],++s2[bl[l]][y]; --s1[bl[l]][bl[x]],++s1[bl[l]][bl[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; --s2[bl[l]][x],++s2[bl[l]][y]; --s1[bl[l]][bl[x]],++s1[bl[l]][bl[y]]; } for(int i=L[bl[r]];i<=r;++i) if(a[i]==x) { a[i]=y; --s2[bl[r]][x],++s2[bl[r]][y]; --s1[bl[r]][bl[x]],++s1[bl[r]][bl[y]]; } build(bl[l]),build(bl[r]); for(int i=bl[l]+1;i<bl[r];++i) { if(!s2[i][x]) continue; if(s2[i][y]) { reset(i); for(int j=L[i];j<=R[i];++j) if(a[j]==x) { a[j]=y; --s2[i][x],++s2[i][y]; --s1[i][bl[x]],++s1[i][bl[y]]; } build(i); } else { s1[i][bl[y]]+=s2[i][x],s1[i][bl[x]]-=s2[i][x]; s2[i][y]+=s2[i][x],s2[i][x]=0; change(i,x,y); } } rebuild(l,x,y); } inline int ask(int l,int r,int k) { int ans=0,sum=0; if(bl[l]==bl[r]) { reset(bl[l]);for(int i=l;i<=r;++i) vec[i]=a[i]; nth_element(vec+l,vec+l+k-1,vec+r+1),ans=vec[l+k-1]; for(int i=l;i<=r;++i) vec[i]=0; return ans; } reset(bl[l]),reset(bl[r]); for(int i=l;i<=R[bl[l]];++i) ++vec[a[i]],++v2[bl[a[i]]]; for(int i=L[bl[r]];i<=r;++i) ++vec[a[i]],++v2[bl[a[i]]]; for(int i=1;i<=bl[N-1];++i) { if(sum+v2[i]+s1[bl[r]-1][i]-s1[bl[l]][i]>=k) { for(int j=(i-1)*p+1;j<=i*p;j++) { if(vec[j]+s2[bl[r]-1][j]-s2[bl[l]][j]+sum>=k) { ans=j;goto FLAG; } else sum+=vec[j]+s2[bl[r]-1][j]-s2[bl[l]][j]; } } else sum+=v2[i]+s1[bl[r]-1][i]-s1[bl[l]][i]; } FLAG: for(int i=l;i<=R[bl[l]];++i) --vec[a[i]],--v2[bl[a[i]]]; for(int i=L[bl[r]];i<=r;++i) --vec[a[i]],--v2[bl[a[i]]]; return ans; } int main() { read(n),read(m),p=sqrt(N)+1; for(int i=1;i<N;++i) bl[i]=(i-1)/p+1; for(int i=1;i<=n;++i) read(a[i]); for(int i=1;i<=bl[n];++i) L[i]=(i-1)*p+1,R[i]=i*p;R[bl[n]]=n; for(int x=1;x<=bl[n];++x) build(x); for(int x=1;x<=bl[n];++x) { for(int i=1;i<N;++i) s2[x][i]=s2[x-1][i]; for(int i=1;i<=bl[N-1];++i) s1[x][i]=s1[x-1][i]; for(int i=L[x];i<=R[x];++i) ++s1[x][bl[a[i]]],++s2[x][a[i]]; } for(;m;--m) { int opt,x,y;read(opt),read(x),read(y); if(opt==1) {int z,w;read(z),read(w),modify(x,y,z,w);} else {int k;read(k),print(ask(x,y,k));} } pr[--lp]=‘\0‘; puts(pr); return 0; }
以上是关于[Ynoi2018]未来日记的主要内容,如果未能解决你的问题,请参考以下文章