hdu5412

题目链接:hdu5412
题意:
这是一道经典的主席树题目,带修改的区间第k大查询。
题解:
我学习了整体二分的方法,以这种常数小,代码短方法来重新解决这个问题。
对于初始数字,变为插入操作
按操作的时间顺序排列各个操作,对于修改操作拆为删除和加入操作:
1 删除之前插入的数字,2. 加入新的数字
接下来分治二分答案:
对于mid,如果插入或者删除的数字<=mid那么应该放到左区间,并且用树状数组(其他数据结构也行)维护前X个位置有多少个数字在左边。
对于询问:如果l,r区间在左边的数字>=k那么答案在左边,否则答案在右边,并且更新K,k=k-这个区间去左边的数字个数
当low =high的时候,说明low就是答案了,只要更新询问还在low,low之间的答案即可。
复杂度分析:分治的深度是log(S)s是数据的范围。
每个询问每次被分到左边或者右边,会有log(s)次操作。每个插入或者删除也是。
但是要用树状数组维护多少个数字在左边,要log(n)次更新或者查询。
复杂度是n*log(s)*log(n)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=300010;
int n,q,cnt;
struct Event{
    int t,type,l,r,num,ans;
}event[maxn];
struct BIT{
    int s[maxn];
    int lowbit(int a){
        return a&(-a);
    }
    void add(int a,int b){
        for (int i=a;i<maxn;i+=lowbit(i)) s[i]+=b;
    }
    int query(int a){
        int ans=0;
        for (int i=a;i;i-=lowbit(i)) ans+=s[i];
        return ans;
    }
}tree;
int store[maxn];
int id[maxn],id2[maxn];
bool cmp(int a,int b){
    return event[a].t<event[b].t;
}
void erfen(int L,int R,int low,int high){
    if (L>R) return;
    if (low==high){
        for (int i=L;i<=R;i++) event[id[i]].ans=low;
        return;
    }
    int l=L,r=R,mid=(low+high)>>1;
    for (int i=L;i<=R;i++)
        if (event[id[i]].type==2){
            int k=tree.query(event[id[i]].r)-tree.query(event[id[i]].l-1);
            if (k>=event[id[i]].num)
                id2[l++]=id[i];
            else{
                event[id[i]].num-=k;
                id2[r--]=id[i];
            }
        }
        else{
            if (event[id[i]].num<=mid){
                tree.add(event[id[i]].l,event[id[i]].type);
                id2[l++]=id[i];
            }
            else id2[r--]=id[i];
        }
    for (int i=L;i<l;i++)
        id[i]=id2[i];
    for (int i=R;i>r;i--)
        id[l+R-i]=id2[i];
    for (int i=L;i<l;i++)
        if (event[id[i]].type!=2)
            tree.add(event[id[i]].l,-event[id[i]].type);
    erfen(L,l-1,low,mid);
    erfen(l,R,mid+1,high);
}
int main(){
    freopen("5412.in","r",stdin);
    freopen("5412.out","w",stdout);
    while (scanf("%d",&n)!=EOF){
        cnt=0;
        for (int i=1;i<=n;i++){
            scanf("%d",&store[i]);
            event[++cnt]=(Event){cnt,1,i,i,store[i],0};
        }
        scanf("%d",&q);
        for (int i=1;i<=q;i++){
            int type;
            scanf("%d",&type);
            if (type==1){
                int a,b;
                scanf("%d%d",&a,&b);
                event[++cnt]=(Event){cnt,-1,a,a,store[a],0};
                store[a]=b;
                event[++cnt]=(Event){cnt,1,a,a,store[a],0};
            }
            else{
                int a,b,c;
                scanf("%d%d%d",&a,&b,&c);
                event[++cnt]=(Event){cnt,2,a,b,c,0};
            }
        }
        for (int i=1;i<=cnt;i++) id[i]=i;
        sort(id+1,id+cnt+1,cmp);
        erfen(1,cnt,1,1000000000);
        for (int i=1;i<=cnt;i++)
            if (event[i].type==2)
                printf("%d\n",event[i].ans);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值