hdu5412 CRB and Queries (整体二分)

题目链接:点这里!!!!


题意:带修改的求区间第K大


题解:

带修改的求区间第K大,很容易想到的一个做法是主席树。


但是注意一下数据的范围,n<=100000。如果用主席树的话,会直接TLE+MLE。


所以主席树在这道题上是不可行的。我们考虑别的做法,我们注意到这道题可以离线处理,基于离线,我们就可以用整体二分来做这道题。


对于一个区间[L,R],我们假设一个数s,求得区间里小于等于s的数的个数为num,如果num>=k,答案肯定小于等于s;如果num<k,答案肯定大于s。


利用这点,我们可以将所有询问一起二分。


我们对所有询问二分答案,然后判断每个询问的答案是否大于当前标准,按照此规定将询问划分成两部分,递归解决。


操作分为三种:

1、给x位置增加一个数v

2、给x位置删除一个数v

3、询问[l,r]中<=mid的数的个数是否<K,mid作为判断的标准。

直接递归就能解决。


如果还有不懂直接看代码吧。


代码:


#include<cstdio>
#include<cstring>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<vector>
#include<bitset>
#include<set>
#include<queue>
#include<stack>
#include<map>
#include<cstdlib>
#include<cmath>
#define PI 2*asin(1.0)
#define LL long long
#define pb push_back
#define pa pair<int,int>
#define clr(a,b) memset(a,b,sizeof(a))
#define lson lr<<1,l,mid
#define rson lr<<1|1,mid+1,r
#define bug(x) printf("%d++++++++++++++++++++%d\n",x,x)
#define key_value ch[ch[root][1]][0]C:\Program Files\Git\bin
const int  MOD = 1000000007;
const int N = 1e5+15;
const int maxn = 3e5+1000;
const int letter = 130;
const int INF = 1e17;
const double pi=acos(-1.0);
const double eps=1e-8;
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
///N 1e5
///maxn 3e5
int n,q,a[maxn],tot;
int p1[maxn],p2[maxn],ans[maxn];
int c[maxn];
struct node{
    int id,l,r,v;
    ///id -1 +1 2
}qu[maxn];
void addqu(int id,int l,int r,int v){
    qu[tot].id=id,qu[tot].l=l,qu[tot].r=r,qu[tot].v=v,p1[tot]=tot++;
}
int lowbit(int x){
    return x&(-x);
}
void update(int x,int val){
    while(x<=n){
        c[x]+=val;
        x+=lowbit(x);
    }
}
int getsum(int x){
    int ans=0;
    while(x>0){
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}
void solve(int L,int R,int low,int high){
    if(L>R) return;
    if(low==high){
        while(L<=R){
            if(qu[p1[L]].id==2) ans[p1[L]]=low;
            L++;
        }
        return;
    }
    int mid=(low+high)/2,j,len;
    int l=L,r=R;
    for(int i=L;i<=R;i++){
        j=p1[i];
        if(qu[j].id==2){
            len=getsum(qu[j].r)-getsum(qu[j].l-1);
            if(len<qu[j].v) qu[j].v-=len,p2[r--]=j;
            else p2[l++]=j;
        }
        else {
            if(qu[j].v<=mid) update(qu[j].l,qu[j].id),p2[l++]=j;
            else p2[r--]=j;
        }
    }
    for(int i=L;i<=R;i++){
        j=p1[i];
        if(qu[j].id!=2&&qu[j].v<=mid) update(qu[j].l,-qu[j].id);
    }
    int i=L;
    for(i=L;i<l;i++)p1[i]=p2[i];
    for(r=R;i<=R;r--,i++) p1[i]=p2[r];
    solve(L,l-1,low,mid);
    solve(l,R,mid+1,high);
}
///hdu 5412
int main(){
    int id,x,y,z;
    while(scanf("%d",&n)!=EOF){
        tot=0;
        int max1=-1;
        for(int i=1;i<=n;i++){
            scanf("%d",a+i);
            addqu(1,i,i,a[i]);
            max1=max(max1,a[i]);
        }
        scanf("%d",&q);
        for(int i=0;i<q;i++){
            scanf("%d%d%d",&id,&x,&y);
            if(id==1){
                addqu(-1,x,x,a[x]);
                a[x]=y;
                addqu(1,x,x,a[x]);
                max1=max(max1,y);
            }
            else {
                scanf("%d",&z);
                addqu(2,x,y,z);
            }
        }
       /// printf("%d\n",max1);
        solve(0,tot-1,1,max1);
        for(int i=0;i<tot;i++) {
            if(qu[i].id==2) {
                printf("%d\n",ans[i]);
            }
        }
    }
    return 0;
}
/*
10
2 100 23 7 2 3 6 3 1 9
7
2 1 10 2
2 1 4 3
1 2 1
2 1 6 1
2 1 10 2
1 4 1000
2 1 9 9
*/




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值