Description
给你
-
1 L R x 将L 到R 加上x -
2 L R k 求L 到R 第k 小的数
Solution
分块大法好!
我们将序列分成
修改整块的就直接打标记,两边的暴力重构该块
关键在查询!
我们可以二分一个答案(思考一下,二分出来的答案会不会序列中不存在呢?)
然后转化成判定性问题,判断二分出来的答案是不是在第
只需要对于每个块,找该块中小于这个值的个数,全部加起来看是不是
证明一下这样为什么不会出现不存在的答案
假设正确答案是
小于等于
所以二分出来的一定是
Code
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define mo 1000000007
using namespace std;
int pl[500],wz[80605],n,m,size;
struct note
{
int x,y;
}a[80605],b[80605];
bool cmp(note x,note y)
{
return x.x<y.x;
}
int nt(int k)
{
return ((k-1)/size+1)*size+1;
}
int pd(int l,int r,int k)
{
int p=nt(l),sum=0,i;
note k1;
k1.x=k;
while (nt(p)<r+1)
{
k1.x-=pl[wz[p]];
sum+=lower_bound(b+p,b+nt(p),k1,cmp)-b-p;
k1.x+=pl[wz[p]];
p=nt(p);
}
if (nt(l)>r)
{
fo(i,l,r) if (a[i].x<k-pl[wz[i]]) sum++;
}
else
{
fo(i,l,nt(l)-1) if (a[i].x<k-pl[wz[i]]) sum++;
fo(i,p,r) if (a[i].x<k-pl[wz[i]]) sum++;
}
return sum;
}
int main()
{
int i,j;
cin>>n;
size=(int)sqrt(n);
fo(i,1,n)
{
scanf("%d",&a[i].x);
b[i].x=a[i].x;
b[i].y=i;
}
j=1;
wz[j]=1;
while (j+size<=n+1)
{
sort(b+j,b+j+size,cmp);
j+=size;
wz[j]=wz[j-size]+1;
}
if(j!=n+1) sort(b+j,b+n+1,cmp);
fo(i,1,n)
{
if (wz[i]==0) wz[i]=wz[i-1];
a[b[i].y].y=i;
}
cin>>m;
int i1=0;
fo(i,1,m)
{
int p,l,r,k,j;
scanf("%d%d%d%d",&p,&l,&r,&k);
if (p==1)
{
j=nt(l);
while (nt(j)<=r)
{
pl[wz[j]]+=k;
j=nt(j);
}
int q;
if (nt(l)>r)
{
fo(q,l,r) a[q].x=b[a[q].y].x=a[q].x+k;
sort(b+nt(l)-size,b+nt(l),cmp);
fo(q,nt(l)-size,nt(l)-1) a[b[q].y].y=q;
}
else
{
fo(q,l,nt(l)-1) a[q].x=b[a[q].y].x=a[q].x+k;
sort(b+nt(l)-size,b+nt(l),cmp);
fo(q,nt(l)-size,nt(l)-1) a[b[q].y].y=q;
fo(q,j,r) a[q].x=b[a[q].y].x=a[q].x+k;
sort(b+j,b+nt(j),cmp);
fo(q,j,nt(j)-1) a[b[q].y].y=q;
}
}
else
{
int x=-5000000,y=5000000,mid;
while (x<y-1)
{
mid=(x+y)/2;
if (pd(l,r,mid)<=k-1) x=mid;
else y=mid;
}
if (pd(l,r,y)==k-1) printf("%d\n",y);
else printf("%d\n",x);
}
}
}