HDU 2852 KiKi's K-Number(动态过程求第K小数)

题意:
给出三种操作,
0 在容器中插入一个数。
1 在容器中删除一个数。

2 求出容器中大于a的第k大元素。

思路:可以用树状数组和线段树,显然a[1]+...+a[i]随i有明显的单调性,所以可以二分出答案

线段树时间复杂度比树状数组的常数大了几倍...所以线段树擦边过了

还有另外一种思路:二分只是二分出a[1]+...+a[i]的上界i,所以可以逆向考虑,从a[1]开始累加,直到到达k,这样的复杂度就由原来的

O(lgN*lgN) 变成O(lgN)了。难在累加的过程,线段树和树状数组是同样的累加方法详见代码


线段树二分代码:

129892102015-02-25 20:49:32Accepted28521965MS4176K2321 BC++ka

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int M = 100010;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
struct node
{
    int l,r;
    int sum;
}tree[M<<2];
int ans = 0;
void build(int l,int r,int rt)//建树
{
    tree[rt].l =l;
    tree[rt].r =r;
    tree[rt].sum =0;
    if(l ==r ) return ;
    int m=(l+r)>>1;

    build(lson);
    build(rson);
}
void up(int rt){
    tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
}

void updata(int l,int r,int rt,int pos,int j)
{
    if(l==r)  {
        tree[rt].sum +=j;
        return;
    }

    int m=(tree[rt].l +tree[rt].r )/2;
    if(pos<=m)
        updata(lson,pos,j);
    else
        updata(rson,pos,j);
    up(rt);
}
int query(int rt,int L,int R)
{
    int m=(tree[rt].l +tree[rt].r)>>1;
    if(L<=tree[rt].l&&tree[rt].r <=R)
    {
        return tree[rt].sum ;
    }
    int ans=0;
    if(L<=m)
        ans+=query(rt<<1,L,R);
    if(R>m)
        ans+=query(rt<<1|1,L,R);
    return ans;
}

int B_search(int k)
{
    int lb = 1;
    int ub = M-1;
    int w=-1;
    while(lb<=ub)
    {
        int m = (lb + ub)>>1;
        ans=query(1,1,m);
        if(ans>=k)
        {
            ub = m - 1;
            w=m;
        }
        else
        {
            lb = m+1;
        }
    }
    return w;
}
int main()
{
    int n,a,b,k;
    while(~scanf("%d",&n))
    {
        build(1,M,1);
        for(int i = 0;i<n;i++)
        {
            scanf("%d",&a);
            if(a==0)
            {
                scanf("%d",&b);
                updata(1,M,1,b,1);
            }
            else if(a==1)
            {
                scanf("%d",&b);
                ans=query(1,b,b);
                if(!ans)
                    puts("No Elment!");
                else
                    updata(1,M,1,b,-1);
            }
            else if(a==2)
            {
                scanf("%d%d",&b,&k);
                ans=query(1,1,b);
                int tmp = B_search(ans+k);
                if(tmp==-1)
                    puts("Not Find!");
                else
                    printf("%d\n",tmp);
            }
        }
    }
    return 0;
}

树状数组二分代码:(可见比线段树快了很多)

//468MS 1484K
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
#define M 100100
#define lowbit(x) (x&-x)
int m;
int C[M];
void update(int rt,int val){
    for(int i=rt;i<M;i+=lowbit(i)){
        C[i]+=val;
    }
}
int sum(int rt){
    int s=0;
    for(int i=rt;i>0;i-=lowbit(i)){
        s+=C[i];
    }
    return s;
}

int main(){
    while(~scanf("%d",&m)){
        int op;
        memset(C,0,sizeof(C));
        while(m--){
            scanf("%d",&op);
            if(op==0){
                int x;
                scanf("%d",&x);
                update(x,1);
            }
            else if(op==1){
                int x;
                scanf("%d",&x);
                if(sum(x)-sum(x-1)==0){
                    printf("No Elment!\n");
                    continue;
                }
                update(x,-1);
            }
            else {
                int x,k;
                scanf("%d%d",&x,&k);
                k+=sum(x);
                int lb=1,ub=M-1;
                while(ub>=lb){
                    int mid=(lb+ub)>>1;
                    if(sum(mid)>=k) ub=mid-1;
                    else lb=mid+1;
                }

                if(lb==M) printf("Not Find!\n");
                else printf("%d\n",lb);
            }
        }
    }
    return 0;
}


逆向累加:(树状数组的累加是二进制的操作,可还原树形图来便于理解)

http://www.cnblogs.com/wuyiqi/archive/2011/12/25/2301071.html 这篇文章有解释

这样果然快了不少,跑到第11名

11ka312MS1484K1420B 

//312MS 1484K
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
#define M 100100
#define lowbit(x) (x&-x)
int m;
int C[M];
void update(int rt,int val){
    for(int i=rt;i<M;i+=lowbit(i)){
        C[i]+=val;
    }
}
int sum(int rt){
    int s=0;
    for(int i=rt;i>0;i-=lowbit(i)){
        s+=C[i];
    }
    return s;
}
int find_kth(int k){
    int ans=0,s=0;
    for(int i=20;i>=0;i--){
        ans+=(1<<i);
        if(ans>=M||s+C[ans]>=k){
            ans-=(1<<i);
        }
        else s+=C[ans];
    }
    return ++ans;
}
int main(){
    while(~scanf("%d",&m)){
        int op;
        memset(C,0,sizeof(C));
        while(m--){
            scanf("%d",&op);
            if(op==0){
                int x;
                scanf("%d",&x);
                update(x,1);
            }
            else if(op==1){
                int x;
                scanf("%d",&x);
                if(sum(x)-sum(x-1)==0){
                    printf("No Elment!\n");
                    continue;
                }
                update(x,-1);
            }
            else {
                int x,k;
                scanf("%d%d",&x,&k);
                k+=sum(x);
                int ans=find_kth(k);
                if(ans==M) printf("Not Find!\n");
                else printf("%d\n",ans);
            }
        }
    }
    return 0;
}


线段树的逆向累加方法也比二分快了不少:

//655MS 2660K
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define M 100100

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

int tree[M<<2];
bool flag;

void up(int rt){
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
bool update(int pos,int val,int l,int r,int rt){
    flag=true;
    if(l==r){
        if(val==-1&&tree[rt]==0){
            flag=false;
            return false;
        }
        tree[rt]+=val;
        return true;
    }
    int m=(l+r)>>1;
    if(pos<=m) update(pos,val,lson);
    else update(pos,val,rson);
    up(rt);
    return flag? true:false;
}
int query(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R){
        return tree[rt];
    }
    int m=(l+r)>>1;
    int ans=0;
    if(L<=m) ans+=query(L,R,lson);
    if(R>m) ans+=query(L,R,rson);
    return ans;
}
void find_kth(int l,int r,int rt,int k)
{
    if(l==r)
    {
        printf("%d\n",l);
        return ;
    }
    int m=(l+r)>>1;
    if(k<=tree[rt<<1]) find_kth(lson,k);
    else find_kth(rson,k-tree[rt<<1]);
}
int main(){
    int m;
    while(~scanf("%d",&m)){
        memset(tree,0,sizeof(tree));
        while(m--){
            int op;
            scanf("%d",&op);
            if(op==0){
                int x;
                scanf("%d",&x);
                update(x,1,1,M-1,1);
            }
            else if(op==1){
                int x;
                scanf("%d",&x);
                if(update(x,-1,1,M-1,1)==false) printf("No Elment!\n");
            }
            else {
                int a,k;
                scanf("%d%d",&a,&k);
                k+=query(1,a,1,M-1,1);
                if(k>tree[1]){
                    printf("Not Find!\n");
                    continue;
                }
                find_kth(1,M-1,1,k);
            }
        }
    }
    return 0;
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值