HDU 2852 KiKi's K-Number(树状数组+二分)

KiKi's K-Number

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3847    Accepted Submission(s): 1707


Problem Description
For the k-th number, we all should be very familiar with it. Of course,to kiki it is also simple. Now Kiki meets a very similar problem, kiki wants to design a container, the container is to support the three operations.

Push: Push a given element e to container

Pop: Pop element of a given e from container

Query: Given two elements a and k, query the kth larger number which greater than a in container;

Although Kiki is very intelligent, she can not think of how to do it, can you help her to solve this problem?
 

Input
Input some groups of test data ,each test data the first number is an integer m (1 <= m <100000), means that the number of operation to do. The next m lines, each line will be an integer p at the beginning, p which has three values:
If p is 0, then there will be an integer e (0 <e <100000), means press element e into Container.

If p is 1, then there will be an integer e (0 <e <100000), indicated that delete the element e from the container  

If p is 2, then there will be two integers a and k (0 <a <100000, 0 <k <10000),means the inquiries, the element is greater than a, and the k-th larger number.
 

Output
For each deletion, if you want to delete the element which does not exist, the output "No Elment!". For each query, output the suitable answers in line .if the number does not exist, the output "Not Find!".
 

Sample Input
  
  
5 0 5 1 2 0 6 2 3 2 2 8 1 7 0 2 0 2 0 4 2 1 1 2 1 2 2 1 3 2 1 4
 

Sample Output
  
  
No Elment! 6 Not Find! 2 2 4 Not Find!

  题意:题目给定三种操作: 0 x 表示把x插入容器 ; 1 x 表示删除一个x如果没有x则输出 No Elment! ; 2 a k 表示比a大的数中的第k大的数 如果没有输出No Find!

思路:网上大神说看到删除元素增加元素就可以想到用树状数组做。。。(做笔记),所以用树状数组写了,其实知道用树桩数组写还蛮好写的,只不过我被卡了一下二分。。求比a大的某个数,就是在后面比a大的数里面找第k个,怎么找呢。。。用sum(x) - sum(now) = k;说明这个区间里有k个元素,如果now = 1的话肯定就是now了,如果now不等于1说明在前面某个点是要求的点。。一般是枚举每个点,sum(i) - sum(x)看哪个点可以让他 = k,用二分优化。。我被卡在,如果这个点有多个值,比如2这个元素有多个,他要求比1这个元素大1的(第二个样例),我的二分是找到 ==k跳出,这样就不可能等于0了,在网上又找到解决方法,如果 sum(mid) -sum(x)> k的时候 sum(mid-1) -sum(x) < k 说明 == k值就在mid里,只不过mid有多个。。。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 5;
int c[maxn], a[maxn];
int lowbit(int k)
{
    return k & -k;
}
void update(int pos, int val)
{
    while(pos < maxn)
    {
        c[pos] += val;
        pos += lowbit(pos);
    }
}
int sum(int pos)
{
    int ans = 0;
    while(pos)
    {
        ans += c[pos];
        pos -= lowbit(pos);
    }
    return ans;
}
int main()
{
    int n, op, r, k;
    while(~scanf("%d", &n))
    {
        memset(c, 0, sizeof(c));
        memset(a, 0, sizeof(a));
        while(n--)
        {
            scanf("%d", &op);
            if(op == 0)
            {
                scanf("%d", &r);
                    a[r]++;
                    update(r, 1);

            }
            if(op == 1)
            {
                scanf("%d", &r);
                if(!a[r]) printf("No Elment!\n");
                else a[r]--, update(r, -1);
            }
            if(op == 2)
            {
                scanf("%d%d", &r, &k);
                int left = r, right = maxn, flag = 0;
                while(left < right)
                {
                    int mid = (left + right) / 2;
                    if(sum(mid) - sum(r) > k)
                    {
                        if(sum(mid-1) - sum(r) < k)
                        {
                            printf("%d\n", mid);
                            flag = 1;
                            break;
                        }
                        right = mid;
                    }
                    if(sum(mid) - sum(r) < k) left = mid + 1;
                    if(sum(mid) - sum(r) == k)
                    {
                        if(a[mid]) {printf("%d\n", mid); flag = 1; break;}
                        else right = mid;
                    }
                }
                if(!flag) printf("Not Find!\n");
            }
        }
    }
    return 0;
}

二分其实还有另一种写法的。。如果sum(maxn) - sum(x) < k说明肯定没有足够的元素,否则肯定有。。肯定有的情况下,二分最后一个符合情况的mid就是答案。。。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 5;
int c[maxn], a[maxn];
int lowbit(int k)
{
    return k & -k;
}
void update(int pos, int val)
{
    while(pos < maxn)
    {
        c[pos] += val;
        pos += lowbit(pos);
    }
}
int sum(int pos)
{
    int ans = 0;
    while(pos)
    {
        ans += c[pos];
        pos -= lowbit(pos);
    }
    return ans;
}
int main()
{
    int n, op, r, k;
    while(~scanf("%d", &n))
    {
        memset(c, 0, sizeof(c));
        memset(a, 0, sizeof(a));
        while(n--)
        {
            scanf("%d", &op);
            if(op == 0)
            {
                scanf("%d", &r);
                    a[r]++;
                    update(r, 1);

            }
            if(op == 1)
            {
                scanf("%d", &r);
                if(!a[r]) printf("No Elment!\n");
                else a[r]--, update(r, -1);
            }
            if(op == 2)
            {
                scanf("%d%d", &r, &k);
                if(sum(maxn) - sum(r) < k)  //说明肯定不符合情况
                {
                    printf("Not Find!\n");
                    continue;
                }
                int left = r, right = maxn, flag = 0;
                while(left < right)
                {
                    int mid = (left + right) / 2;
                    if(sum(mid) - sum(r) < k) left = mid + 1;
                    else right = mid;  //注意这里是=mid 不是mid-1,因为这里是符合情况的,可能前面是 == k,如果是 ==k mid就是答案了
                }<span style="white-space:pre">		</span>//如果分情况多一点,不可能有 == k的情况就是mid - 1,因为mid已经不符合了,二分还要深刻理解啊!
                printf("%d\n", right);
            }
        }
    }
    return 0;
}


网上还有一种代码,不用二分,,,我觉得看看就好。。

关键代码:

int find_kth(int k){
    int ans = 0, cnt = 0, i;
    for (i = 20; i >= 0; i--){
        ans += (1 << i);
        if (ans >= maxn|| cnt + c[ans] >= k)
			ans -= (1 << i);
        else cnt += c[ans];
    }
    return ans + 1;
}




  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值