1. 程式人生 > >【OJ2130】K小數查詢

【OJ2130】K小數查詢

很好 print char line 分塊 con 多少 第一個 ()

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); 
        }
    }
}

【OJ2130】K小數查詢