P4119 [Ynoi2018] 未来日记
\(\text{Links}\)
题外话
-
个人生涯中第一道独立通过的 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;
}