有一种常数比较小的
O(nn−−√log2n)
O
(
n
n
log
2
n
)
的做法。
分块,每个块维护一个其中元素排好序之后的数组。修改的时候零散块直接重构,整块打标记。询问的时候先二分答案
mid
m
i
d
,那么就转化成求小于等于
mid
m
i
d
的数的个数,对于零散的块重构后暴力数,整块的如果标记
≤mid
≤
m
i
d
答案就是块大小,否则直接在数组上二分即可。
如果把排序的数组换成权值线段树,二分答案换成若干棵权值线段树上一起二分,复杂度可以降到 O(nn−−√logn) O ( n n log n ) ,但常数很大。。。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 90010
#define ID(x) ((x-1)/B+1)
#define min(x,y) (x<y?x:y)
using namespace std;
const int B=520;
const int inf=0x3f3f3f3f;
int n,m,a[N],sta[20],t[(B+10)<<1],top;
int read()
{
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
void write(int x)
{
int pot=0;
if(x==0) {putchar('0');return ;}
for(;x;x/=10) sta[++pot]=x%10;
while(pot) putchar('0'+sta[pot--]);
}
struct block
{
int L,R,len,mi,s[B+10];
bool ex;
block(){mi=inf;ex=0;}
void rebuild()
{
if(!ex) return ;
for(int i=L;i<=R;i++)
s[i-L+1]=a[i]=min(a[i],mi);
sort(s+1,s+len+1);
ex=0;
}
void build(int l,int r)
{
ex=1;
L=l;R=r;
len=r-l+1;
rebuild();
}
void mdf(int lab)
{
if(lab<mi) ex=1,mi=lab;
}
void bmdf(int l,int r,int lab)
{
ex=1;
for(int i=l;i<=r;i++)
a[i]=min(a[i],lab);
rebuild();
}
int qry(int x)
{
if(mi<=x) return len;
return upper_bound(s+1,s+len+1,x)-s-1;
}
int bqry(int l,int r,int x)
{
int re=0;
for(int i=l;i<=r;i++)
re+=(a[i]<=x);
return re;
}
}blk[N/B];
bool check(int x,int l,int r,int idl,int idr,int k)
{
int re=0;
re+=upper_bound(t+1,t+top+1,x)-t-1;
for(int i=idl+1;i<idr&&re<k;i++)
{
re+=blk[i].qry(x);
if(r-blk[i].R+re<k) break;
}
return (re>=k);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1,id=1;i<=n;i+=B,id++)
blk[id].build(i,min(i+B-1,n));
while(m--)
{
int opt=read(),l=read(),r=read(),k=read(),idl=ID(l),idr=ID(r);
if(opt==1)
{
if(idl==idr) blk[idl].bmdf(l,r,k);
else
{
blk[idl].bmdf(l,blk[idl].R,k);
blk[idr].bmdf(blk[idr].L,r,k);
for(int i=idl+1;i<idr;i++)
blk[i].mdf(k);
}
}
else
{
blk[idl].rebuild();
blk[idr].rebuild();
top=0;
if(idl==idr)
for(int i=l;i<=r;i++)
t[++top]=a[i];
else
{
for(int i=l;i<=blk[idl].R;i++)
t[++top]=a[i];
for(int i=blk[idr].L;i<=r;i++)
t[++top]=a[i];
}
sort(t+1,t+top+1);
int lc=0,rc=inf;
while(lc<rc)
{
int mid=(lc+rc>>1);
if(check(mid,l,r,idl,idr,k)) rc=mid;
else lc=mid+1;
}
write(lc);
putchar('\n');
}
}
return 0;
}