2130 -- K小数查询(Solution)
题目大意 : 给你一个长度为 \(N\) 的数列和 \(Q\) 个操作,操作包括:①区间加一个数;②询问区间内第 \(k\) 小的数。\((n,q\le80000)\) .
Tag: 二分、分块
Analysis By LC:
我知道这题可以用主席树做,但像我这么菜的咸鱼选手怎么可能会主席树。所以我们用分块。
第一个操作时分块常规操作,但第二个看起来不是很好做。我们可以尝试用二分答案转化问题求解,:一个数有至少 \(k-1\) 个数比它小,该数最小值即为所求。那么我们只需对于每个块,找出有多少个数比该数小就行了。我们可以维护每一块排序后的数列,查询时仅需用STL的 \(bound\) 系列查找函数即可。
虽然是分块套路题,但个人认为细节还是不少,可能是我太菜了吧。
Code By LC :
#include<cstdio>
#include<algorithm>
using namespace std;
inline int _read()
{
char c; int x=0,f=1;
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
return x*f;
}
const int N=80005,INF=5000000;
int n,a[N],ind,l[N],r[N],id[N],ta[N],isl[N],pos[N];
struct node
{
int id,w;
bool operator < (const node x) const {
return w < x.w;
}
} s[N];
void rebuild(int x)
{
sort(s+l[x],s+1+r[x]);
for(int i=l[x];i<=r[x];i++)
pos[s[i].id]=i;
}
bool check(int mid, int ql, int qr, int x)
{
int k=0,i;
for(;ql<=qr&&!isl[ql];ql++)k+=(s[pos[ql]].w+ta[id[ql]]<=mid);
if(ql>n||ql>qr) return k>=x;
for(i=isl[ql];i<=ind&&r[i]<=qr;i++)
{
int tmp=upper_bound(s+l[i],s+1+r[i],(node){-1,mid-ta[i]})-s;
k+=tmp-l[i];
}
if(i>ind) return k>=x;
for(ql=l[i];ql<=qr;ql++)k+=(s[pos[ql]].w+ta[id[ql]]<=mid);
return k>=x;
}
int main()
{
n=_read();
const int m=sqrt(n);
for(int i=1;i<=n;i=min(n,i+m-1)+1)
{
l[++ind]=i,r[ind]=min(n,i+m-1);
isl[i]=ind;
}
for(int i=1;i<=ind;i++)
for(int j=l[i];j<=r[i];j++) id[j]=i;
for(int i=1;i<=n;i++) s[i]=(node){i,_read()};
for(int i=1,k=0;i<=ind;i++) sort(s+l[i],s+1+r[i]);
for(int i=1;i<=n;i++) pos[s[i].id]=i;
int q=_read();
while(q--)
{
int op=_read(),ql=_read(),qr=_read(),x=_read();
if(op==1)
{
int i;
for(;ql<=qr&&!isl[ql];ql++) s[pos[ql]].w+=x;
rebuild(id[ql-1]);
if(ql>qr) continue;
for(i=isl[ql];i<=ind&&r[i]<=qr;i++)ta[i]+=x;
if(i<=ind) for(ql=l[i];ql<=qr;ql++) s[pos[ql]].w+=x;
rebuild(i);
}
if(op==2)
{
int tl=-INF,tr=INF;
while(tl<=tr)
{
int mid=tl+tr>>1;
if(check(mid,ql,qr,x)) tr=mid-1;
else tl=mid+1;
}
printf("%d\n",tr+1);
}
}
}
原文地址:https://www.cnblogs.com/farway17/p/9416875.html
时间: 2024-10-27 16:08:46