Description
维护一个长度为n的序列,该序列支持Q个操作:
①将第L到R个数加上x
②询问L到R之间第k小个数是什么。
Solve
看到实现时间7000ms,嘿嘿,果断分块打法。~~
大呼:分块大法好
这题因为有区间加操作,直接用数据结构难以维护区间 K 小值。但是可以用
分块解决此题。最简单的方法是每个块维护原块和排序后的块,询问时二分答案,
话说这时间复杂度有点玄学啊。。汗。。(⊙﹏⊙)
也可用分块套线段树但常数较大。 蒟蒻表示Σ( ° △ °|||)︴
“本题数据范围对于分块题来说较大,时间较长,是为了区分分块和暴力。”——题解
这是什么鬼。。好几次吓得我以为超时了。。
Code
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define INF 5000000
#define N 80005
#define L(x) (x-1)*block+1
#define R(x) min(x*block,n)
using namespace std;
int n,block,a[N],b[N],wz[N],c[N],MAX=0;
void Px(int x)
{
fo(i,L(x),R(x)) b[i]=a[i];
sort(b+L(x),b+R(x)+1);
}
void Refresh(int l,int r,int x)
{
if (wz[l]==wz[r])
{
fo(i,l,r) a[i]+=x;
Px(wz[l]);
}
else
{
fo(i,wz[l]+1,wz[r]-1) c[i]+=x;
fo(i,l,R(wz[l])) a[i]+=x; Px(wz[l]);
fo(i,L(wz[r]),r) a[i]+=x; Px(wz[r]);
}
}
int Find(int l,int r,int x)
{
int ans=0;
if (wz[l]==wz[r]) fo(i,l,r) ans+=(a[i]+c[wz[i]]<=x);
else
{
fo(i,wz[l]+1,wz[r]-1) ans+=upper_bound(b+L(i),b+R(i)+1,x-c[i])-(b+L(i));
fo(i,l,R(wz[l])) ans+=(a[i]+c[wz[i]]<=x);
fo(i,L(wz[r]),r) ans+=(a[i]+c[wz[i]]<=x);
}
return ans;
}
int Binary_search(int ll,int rr,int x)
{
int l=-INF,r=INF;
while (l<r-1)
{
int mid=(l+r)/2;
if (Find(ll,rr,mid)<x) l=mid;
else r=mid;
}
return r;
}
int main()
{
freopen("data.in","r",stdin);
freopen("4438.out","w",stdout);
scanf("%d",&n);
block=sqrt(n);
int m=n/block+(n%block?1:0);
fo(i,1,n)
{
scanf("%d",&a[i]);
b[i]=a[i];
wz[i]=(i-1)/block+1;
}
fo(i,1,m) sort(b+L(i),b+R(i)+1);
int Q;
scanf("%d",&Q);
int mark,l,r,x;
while (Q--)
{
scanf("%d%d%d%d",&mark,&l,&r,&x);
if (mark==1) Refresh(l,r,x);
else printf("%d\n",Binary_search(l,r,x));
}
}