• HDU 2852 KiKi's KNumber


    题目传送门

    一、解题思路

    题意:题目给定三种操作:

    0 x 表示把x插入容器 ;
    1 x 表示删除一个x如果没有x则输出 No Elment! ;
    2 a k 表示比a大的数中的第k大的数 如果没有输出No Find!

    思路:
    树状数组维护元素出现次数(权值)前缀和即可。
    操作\(0\)即修改;
    操作\(1\)先查询\(x\)是否存在,如果存在删除一个即可。
    操作\(2\)可以用二分逐渐逼近答案。

    二、简单二分

    #include <iostream>
    #include <string.h>
    #include <stdio.h>
    #include <vector>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <math.h>
    #include <cstdio>
    typedef long long LL;
    using namespace std;
    
    #define lowbit(x) (x & -x)
    const int M = 100010, N = 100000;
    int t[M];
    
    //小于等于v的元素的数目
    int getsum(int x) {
        int s = 0;
        while (x) s += t[x], x -= lowbit(x);
        return s;
    }
    //将值为x的元素增加v个
    void update(int x, int v) {
        while (x <= N) t[x] += v, x += lowbit(x);
    }
    
    int main() {
        int m;
        while (~scanf("%d", &m)) {
            memset(t, 0, sizeof(t));
            while (m--) {
                int op;
                scanf("%d", &op);
                if (op == 0) {
                    int x;
                    scanf("%d", &x);
                    update(x, 1);
                } else if (op == 1) {
                    int x;
                    scanf("%d", &x);
                    if (getsum(x) - getsum(x - 1) == 0)
                        printf("No Elment!\n");
                    else
                        update(x, -1);
                } else {
                    int a, k;
                    scanf("%d %d", &a, &k);
                    int x = getsum(a);
                    if (getsum(N) - x < k)
                        printf("Not Find!\n");
                    else {
                        int l = a, r = N;
                        while (l < r) {
                            int mid = (l + r) >> 1;
                            if (getsum(mid) - x >= k)
                                r = mid;
                            else
                                l = mid + 1;
                        }
                        printf("%d\n", l);
                    }
                }
            }
        }
        return 0;
    }
    

    三、以二进制思想查找第\(k\)大的数

    首先树状数组\(t[i]\)里面存的是在\(i\)管辖的范围内各个组数的和,比如\(1\)出现\(2\)次,\(2\)出现\(3\)次,\(4\)出现\(6\)次:
    那么\(a[1]=2,a[2]=3,a[3]=0,a[4]=6\);故\(t[4]=11\);所以总的元素如下\(\{1,1,2,2,2,4,4,4,4,4,4\}\)

    知道了\(a[]\)数组的含义后(\(a[x]\)表示\(x\)出现的次数),我们再来看看树状数组求和的过程:

    假设我们要求\(sum[15] (a[1] + …… + a[15])\),根据树状数组巧妙的求和运算,
    依次累加\(t[1111\)(二进制表示)\(],t[1110],t[1100],t[1000]\)
    反过来看,利用二进制,从高位到地位确定当前位是\(1\)还是\(0\),首先假设是\(1\),判断累计结果是否会超过\(k\),超过\(k\)则假设不成立,应为\(0\),继续确定下一位。基于二进制最后可以确定第\(k\)小的数的数值。

    (注意:利用树状数组求第\(k\)小的元素的时候是根据总的元素的,例如上面举例的{\(1,1,2,2,2,4,4,4,4,4,4\)},而不是在\(a[1],a[2],a[3],a[4]\)中去寻求第\(k\)小,并且求第\(k\)小时候包含重复元素的数,没有直接跳过重复元素,例如还是上面的例子第\(2\)小还是\(1\)

    #include <iostream>
    #include <string.h>
    #include <stdio.h>
    #include <vector>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <math.h>
    #include <cstdio>
    using namespace std;
    
    #define lowbit(x) (x & -x)
    const int N = 100010;
    
    int t[N];
    
    //小于等于v的元素的数目
    int getsum(int x) {
        int s = 0;
        while (x) s += t[x], x -= lowbit(x);
        return s;
    }
    //将值为x的元素增加v个
    void update(int x, int v) {
        while (x <= N) t[x] += v, x += lowbit(x);
    }
    
    //寻找第k大的模板
    int find_kth(int k) {
        int ans = 0, sum = 0;
        for (int i = 20; i >= 0; i--) {          //这里的20适当的取值,与MAX_VAL有关,一般取lg(MAX_VAL)
            ans += (1 << i);                     //答案一定可以表示成二进制数的形式
            if (ans >= N || sum + t[ans] >= k)   //寻找小于k的最大的ans
                ans -= (1 << i);
            else
                sum += t[ans];
        }
        return ans + 1; // ans是小于k的最大的数 那么ans+1必定是第k大的数
    }
    
    int main() {
        int m;
        while (~scanf("%d", &m)) {
            memset(t, 0, sizeof(t));
            while (m--) {
                int op;
                scanf("%d", &op);
                if (op == 0) {
                    int x;
                    scanf("%d", &x);
                    update(x, 1);
                } else if (op == 1) {
                    int x;
                    scanf("%d", &x);
                    if (getsum(x) - getsum(x - 1) == 0) //若x存在 则getsum(x)-getsum(x-1)至少为1
                        printf("No Elment!\n");
                    else
                        update(x, -1); //存在则删除一个
                } else {
                    int a, k;
                    scanf("%d %d", &a, &k);
                    int x = getsum(a);
                    if (getsum(N) - x < k) // getsum(maxn)=当前序列元素个数
                        printf("Not Find!\n");
                    else {
                        k += x;
                        printf("%d\n", find_kth(k));
                    }
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Django框架之虚拟环境搭建
    Ubantu16.04系统优化
    关于装双系统Ubantu16.04+Win10引导问题
    网络编程相关
    数据库常用语句
    javascript异步编程的六种方式
    关于 CSS 的一些小 tips
    typeof 返回的数据类型 及 javascript数据类型中的一些小知识点
    JavaScript || 和 && 的总结
    正则表达式
  • 原文地址:https://www.cnblogs.com/littlehb/p/16225915.html
Copyright © 2020-2023  润新知