【前言】
年初一写的题,原因是熬夜太晚了,写几题分块醒醒神(雾
【题目】
BZOJ
长度为
n
n
n序列
A
A
A,支持
m
m
m次两种操作:
- 将区间内值为 x x x的位置修改为 y y y
- 询问区间第
k
k
k大
n , m , A i ≤ 1 0 5 n,m,A_i\leq 10^5 n,m,Ai≤105
【解题思路】
不可做题考虑分块暴力。
先考虑如何求区间第
k
k
k小。我们对序列和权值都进行分块,设
n
u
m
i
,
j
num_{i,j}
numi,j表示在序列前
j
j
j块中,在权值前
i
i
i块内的数字个数,
c
n
t
i
,
j
cnt_{i,j}
cnti,j表示序列前
j
j
j块中数字
i
i
i出现次数。对于一个询问
[
l
,
r
]
[l,r]
[l,r],先将散块加入临时数组
t
n
u
m
,
t
c
n
t
tnum,tcnt
tnum,tcnt中,然后枚举答案在哪一个权值块中,接着暴力枚举答案可以在
O
(
n
)
O(\sqrt n)
O(n)的时间内求出答案。
接下来考虑如何实现区间改值。对于散块我们可以直接暴力重构整块。对于整块,若这一块中没有 x x x可以跳过,若没有 y y y,则直接将 x x x映射成 y y y,若两者都有,则暴力重构整块。
暴力重构?我们考虑什么情况下会重构,即在一个块内发生了一次合并。长度为 n n n的序列最多只会合并 O ( n ) O(n) O(n)次(因为会使不同的数字个数 − 1 -1 −1),而每次修改对两个散块各提供了一次合并机会。那么总的合并次数就是 O ( n + m ) O(n+m) O(n+m)的了。
具体实现上,每块中的转换情况是若干条不相交的链,只需要记录每个初值转换后是什么,以及每个现在的值对于哪个初值即可。查询的时候我们需要知道散块每个位置的值,这里可以直接重构两块,再遍历一遍原数组即可。修改时还要维护 n u m , c n t num,cnt num,cnt,由于只有两个值, j j j这一维是可以暴力维护的。
最后复杂度 O ( ( n + m ) n ) O((n+m)\sqrt n) O((n+m)n)
写一下,调一年。luogu卡不过告辞
我又写了奇怪的内存出来。。。一定要注意了!!!
【参考代码】
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,lim=320,Blo=lim+5;
namespace IO
{
inline char gc()
{
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
int ret=0;char c=gc();
while(!isdigit(c)) c=gc();
while(isdigit(c)) ret=ret*10+(c^48),c=gc();
return ret;
}
inline void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
inline void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;
namespace DreamLolita
{
int n,m;
int a[N],bl[N];
int pos[N],num[Blo][Blo],cnt[Blo][N],tcnt[N],tnum[Blo];//use overflow memory because like cnt[lim][N]
struct Block
{
int st,ed,id[N],rid[Blo];
void init()//get real number
{
for(int i=st;i<=ed;++i) a[i]=rid[pos[i]];
}
void change(int x,int y){int uid=id[x];id[y]=uid;rid[uid]=y;id[x]=0;}
void build()//init id
{
for(int i=1;i<=lim;++i) id[rid[i]]=0;
for(int i=st,ind=0;i<=ed;++i) if(!id[a[i]])
id[a[i]]=++ind,rid[ind]=a[i];
for(int i=st;i<=ed;++i) pos[i]=id[a[i]];
}
}B[lim];
void rebuild(int p,int x,int y)//get the prefix sum
{
//cerr<<p<<" "<<x<<" "<<y<<endl;
for(int i=bl[p];i<=bl[n];++i)
{
num[i][bl[x]]+=num[i-1][bl[x]];num[i][bl[y]]+=num[i-1][bl[y]];
cnt[i][x]+=cnt[i-1][x];cnt[i][y]+=cnt[i-1][y];
}
}
void brute(int l,int r,int x,int y)//update the sporadic blocks && the changed blocks
{
for(int i=l;i<=r;++i) if(a[i]==x)
{
a[i]=y;
--num[bl[l]][bl[x]];++num[bl[l]][bl[y]];
--cnt[bl[l]][x];++cnt[bl[l]][y];
}
}
void update(int l,int r,int x,int y)
{
if(cnt[bl[r]][x]-cnt[bl[l]-1][x]==0) return;
for(int i=bl[n];i>=bl[l];--i)
{
num[i][bl[x]]-=num[i-1][bl[x]];num[i][bl[y]]-=num[i-1][bl[y]];
cnt[i][x]-=cnt[i-1][x];cnt[i][y]-=cnt[i-1][y];
}//undo the prefix sum
if(bl[l]==bl[r])
{
B[bl[l]].init();brute(l,r,x,y);
B[bl[l]].build();rebuild(l,x,y);
return;
}
B[bl[l]].init();B[bl[r]].init();
brute(l,B[bl[l]].ed,x,y);brute(B[bl[r]].st,r,x,y);
B[bl[l]].build();B[bl[r]].build();
for(int i=bl[l]+1;i<bl[r];++i)
{
if(!cnt[i][x]) continue;
if(!cnt[i][y])
{
num[i][bl[y]]+=cnt[i][x];num[i][bl[x]]-=cnt[i][x];
cnt[i][y]+=cnt[i][x];cnt[i][x]=0;
B[i].change(x,y);
}//map x to to y
else
{
B[i].init();brute(B[i].st,B[i].ed,x,y);B[i].build();
}//bruteforce to merge two number
}//update the integral blocks
rebuild(l,x,y);
}
int query(int l,int r,int k)
{
int res=0,tot=0;
if(bl[l]==bl[r])
{
B[bl[l]].init();for(int i=l;i<=r;++i)tcnt[i]=a[i];
nth_element(tcnt+l,tcnt+l+k-1,tcnt+r+1);res=tcnt[l+k-1];
for(int i=l;i<=r;++i) tcnt[i]=0;
return res;
}
B[bl[l]].init();B[bl[r]].init();
for(int i=l;i<=B[bl[l]].ed;++i) ++tcnt[a[i]],++tnum[bl[a[i]]];
for(int i=B[bl[r]].st;i<=r;++i) ++tcnt[a[i]],++tnum[bl[a[i]]];
for(int i=1;i<=bl[N-1];++i)
{
if(tot+tnum[i]+num[bl[r]-1][i]-num[bl[l]][i]>=k)
{
for(int j=B[i].st;j<=B[i].ed;++j)
{
if(tcnt[j]+cnt[bl[r]-1][j]-cnt[bl[l]][j]+tot>=k){res=j;goto OK;}
else tot+=tcnt[j]+cnt[bl[r]-1][j]-cnt[bl[l]][j];
}
}
else tot+=tnum[i]+num[bl[r]-1][i]-num[bl[l]][i];
}
OK:
for(int i=l;i<=B[bl[l]].ed;++i) --tcnt[a[i]],--tnum[bl[a[i]]];
for(int i=B[bl[r]].st;i<=r;++i) --tcnt[a[i]],--tnum[bl[a[i]]];
return res;
}
void solution()
{
for(int i=1;i<N;++i) bl[i]=(i-1)/lim+1;
n=read();m=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=bl[n];++i) B[i].st=(i-1)*lim+1,B[i].ed=i*lim; B[bl[n]].ed=n;
for(int i=1;i<=bl[n];++i) B[i].build();
for(int i=1;i<=bl[n];++i)
{
memcpy(num[i],num[i-1],sizeof(num[i]));
memcpy(cnt[i],cnt[i-1],sizeof(cnt[i]));
for(int j=B[i].st;j<=B[i].ed;++j) ++num[i][bl[a[j]]],++cnt[i][a[j]];
}
while(m--)
{
int op=read(),l=read(),r=read(),x,y;
if(op&1) x=read(),y=read(),update(l,r,x,y);
else x=read(),writeln(query(l,r,x));
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ5145.in","r",stdin);
freopen("BZOJ5145.out","w",stdout);
#endif
DreamLolita::solution();
return 0;
}