#分块,二分#洛谷 5356 [Ynoi2017] 由乃打扑克
题目
支持区间加和区间查询第 \(k\) 小
分析
分块之后给每个整块排序,这样修改的时候整块打标记,散块直接分开把需要加的部分暴力加之后归并,就是 \(O(\sqrt{n})\) 的
查询的话,如果只有散块直接归并出结果,否则二分答案,加上小块合并成的整块,相当于是整体二分,就是 \(O(\sqrt{n}\log{a_i})\) 的
理论上块长取 \(\sqrt{n}\log{n}\) 实际上直接取根号更快
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=100011;
int n,Q,a[N],lazy[N],b[N],rk[N],rK[N],Rk[N],L[N],R[N],pos[N],bl,_l[N],_r[N],_mid[N];
int iut(){
int ans=0,f=1; char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans*f;
}
void print(int ans){
if (ans<0) putchar('-'),ans=-ans;
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
bool cmp(int x,int y){return a[x]<a[y];}
void rebuild(int x,int l,int r,int z){
Rk[0]=rK[0]=0;
for (int i=L[x];i<=R[x];++i)
if (l<=rk[i]&&rk[i]<=r) Rk[++Rk[0]]=rk[i],a[rk[i]]+=z;
else rK[++rK[0]]=rk[i];
int i1=1,j1=1,j=L[x];
while (i1<=Rk[0]&&j1<=rK[0])
if (a[Rk[i1]]<a[rK[j1]]) rk[j++]=Rk[i1++];
else rk[j++]=rK[j1++];
while (i1<=Rk[0]) rk[j++]=Rk[i1++];
while (j1<=rK[0]) rk[j++]=rK[j1++];
}
int main(){
n=iut(),Q=iut(),bl=sqrt(n);
for (int i=1;i<=n;++i) a[i]=iut(),pos[i]=(i-1)/bl+1,rk[i]=i;
for (int i=1;i<=n;++i) if (!L[pos[i]]) L[pos[i]]=i;
for (int i=n;i>=1;--i) if (!R[pos[i]]) R[pos[i]]=i;
for (int i=1;i<=pos[n];++i) sort(rk+L[i],rk+R[i]+1,cmp);
for (int i=1;i<=Q;++i){
int opt=iut(),l=iut(),r=iut(),z=iut();
if (opt==1){
if (r-l+1<z||z<=0){
print(-1),putchar(10);
continue;
}
int ans=-1;
if (pos[l]==pos[r]){
for (int j=L[pos[l]];j<=R[pos[r]]&&z;++j)
if (l<=rk[j]&&rk[j]<=r) ans=a[rk[j]]+lazy[pos[l]],--z;
print(ans),putchar(10);
}else{
Rk[0]=rK[0]=0;
for (int j=L[pos[l]];j<=R[pos[l]];++j)
if (rk[j]>=l) Rk[++Rk[0]]=a[rk[j]]+lazy[pos[l]];
for (int j=L[pos[r]];j<=R[pos[r]];++j)
if (rk[j]<=r) rK[++rK[0]]=a[rk[j]]+lazy[pos[r]];
int i1=1,j1=1,len=0;
while (i1<=Rk[0]&&j1<=rK[0])
if (Rk[i1]<rK[j1]) b[++len]=Rk[i1++];
else b[++len]=rK[j1++];
while (i1<=Rk[0]) b[++len]=Rk[i1++];
while (j1<=rK[0]) b[++len]=rK[j1++];
if (pos[l]+1==pos[r]) print(b[z]),putchar(10);
else{
int mn=b[1],mx=b[len],cnt=0;
for (int j=pos[l]+1;j<pos[r];++j){
mn=min(mn,a[rk[L[j]]]+lazy[j]);
mx=max(mx,a[rk[R[j]]]+lazy[j]);
_l[j]=L[j],_r[j]=R[j];
}
_l[0]=1,_r[0]=len;
while (mn<mx){
int mid=(0ll+mn+mx)>>1,_L,_R,CNT=cnt;
if (_l[0]<=_r[0]){
_L=_l[0]-1,_R=_r[0];
while (_L<_R){
int _MID=(_L+_R+1)>>1;
if (b[_MID]<=mid) _L=_MID;
else _R=_MID-1;
}
_mid[0]=_L,cnt+=_L-_l[0]+1;
}
for (int j=pos[l]+1;j<pos[r];++j)
if (_l[j]<=_r[j]){
_L=_l[j]-1,_R=_r[j];
while (_L<_R){
int _MID=(_L+_R+1)>>1;
if (a[rk[_MID]]+lazy[j]<=mid) _L=_MID;
else _R=_MID-1;
}
_mid[j]=_L,cnt+=_L-_l[j]+1;
}
if (cnt<z){
mn=mid+1;
if (_l[0]<=_r[0]) _l[0]=_mid[0]+1;
for (int j=pos[l]+1;j<pos[r];++j)
if (_l[j]<=_r[j]) _l[j]=_mid[j]+1;
}else{
mx=mid;
if (_l[0]<=_r[0]) _r[0]=_mid[0];
for (int j=pos[l]+1;j<pos[r];++j)
if (_l[j]<=_r[j]) _r[j]=_mid[j];
cnt=CNT;
}
}
print(mn),putchar(10);
}
}
}else{
if (pos[l]==pos[r]){
if (L[pos[l]]==l&&R[pos[r]]==r) lazy[pos[l]]+=z;
else rebuild(pos[l],l,r,z);
}
else{
if (l==L[pos[l]]) lazy[pos[l]]+=z;
else rebuild(pos[l],l,R[pos[l]],z);
if (r==R[pos[r]]) lazy[pos[r]]+=z;
else rebuild(pos[r],L[pos[r]],r,z);
for (int j=pos[l]+1;j<pos[r];++j) lazy[j]+=z;
}
}
}
return 0;
}