树状数组求第k大(二分做法)

由于看不懂位运算版的树状数组求第k大,只能学学二分,优秀的log²复杂度,可以接受!
算法核心:由于树状数组自带求比自己小的数有几个,我们可以求第k小,
公式:第k大 可以转化成 = 求第(n(元素总个数) - k + 1)小

例如求1 2 8 10 12,第2大,我们是不是转化成求第3小。话不多说,直接上题就懂了。

1、The k-th Largest Group

传送门
Des
初始有n个猫舍,每个猫舍都有一只猫,有两种操作。
(1)合并x,y两个猫舍的猫
(2)询问数量第k大的猫舍有几只猫
Solution
我们使用树状数组维护猫舍数量出现的次数,c[i]表示数量为i的猫舍有几个。集合之间的关系使用并查集处理即可。
Code

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 3e5 + 10;
int n, m;
int a[N], p[N], c[N];
inline int lowbit(int x)
{
    return x & (-x);
}
inline void change(int i, int k)
{
    for( ; i <= n; i += lowbit(i)) c[i] += k;
}
int query(int i)
{
    int res = 0;
    for( ; i; i -= lowbit(i))  res += c[i];
    return res;
}
int findd(int x)
{
    while(x != p[x]) x = p[x] = p[p[x]];
    return x;
}
signed main()
{
    scanf("%d%d", &n, &m);
    int num = n;
    for(int i = 1; i <= n; ++i) p[i] = i;
    for(int i = 1; i <= n; ++i) a[i] = 1;
    change(1, n);
    int op, x, y;
    while(m-- && scanf("%d", &op))
    {
        if(op == 0)
        {
            scanf("%d%d", &x, &y);
            x = findd(x);
            y = findd(y);
            if(x == y) continue;
            change(a[x], -1);
            change(a[y], -1);
            p[y] = x;
            a[x] += a[y];
            change(a[x], 1);
            num--;
        }
        else
        {
            scanf("%d", &x);
            int k = num - x + 1;
            int l = 1, r = n;
            while(l < r)
            {
                int mid = l + r >> 1;
                if(query(mid) >= k) r = mid;
                else l = mid + 1;
            }
            printf("%d\n", l);
        }
    }
    return 0;
}

2、KiKi's K-Number

Des
传送门
题目变成了求比某个数x大的第k大是谁?然后添加了两种操作
(1)插入一个数
(2)删除一个数
Solution
直接二分x以后的区间,不断缩小大于等于k的范围
Code

#include <bits/stdc++.h>
#define MX 1e5
using namespace std;
const int N = 1e5 + 10;
int q, c[N];
inline int lowbit(int x)
{
    return x & (-x);
}
inline void change(int i, int k)
{
    for(; i <= MX; i += lowbit(i)) c[i] += k;
}
inline int query(int i)
{
    int res = 0;
    for( ; i; i -= lowbit(i)) res += c[i];
    return res;
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0);
    while(cin >> q)
    {
        memset(c, 0, sizeof c);
        int op, x, k;
        while(q-- && cin >> op)
        {
            if(op == 0)
            {
                cin >> x;
                change(x, 1);
            }
            else if(op == 1)
            {
                cin >> x;
                if(query(x) - query(x - 1) == 0) cout << "No Elment!\n";
                else change(x, -1);
            }
            else
            {
                cin >> x >> k;
                int cmp = query(x);
                if(query(MX) - cmp < k) cout << "Not Find!\n";//如果之间的数不足k个,不存在
                else
                {
                    int l = 1, r = MX;
                    while(l < r)
                    {
                        int mid = l + r >> 1;
                        if(query(mid) - cmp >= k) r = mid;//不断逼近k个
                        else l =  mid + 1;
                    }
                    cout << l << "\n";
                }
            }
        }
    }
    return 0;
}

posted @   std&ice  阅读(580)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示