求第K小数(树状数组+二分)2021icpc河南省省赛赛

补题做到的,没有注意提交人数,
看到题就有一点思路就开始敲了,可以转化为经典的主席树问题,区间查询第k小,但是树状数组代码比较简短;

题目大意

给你一个数组,给了一个k,让你答出,每个数左边第k个比他大的数是多少。

题解

先把所有的数据用结构体或者pair记录原下标,按照权值从大到小排序,转化为离线操作。
从大到小插入数据,每组数据就相当于是一个询问,这样可以保证插入这个数据之前的所有数据都比他大。
所以可以用树状数组去记录权值,也就是他左边出现的数的数量,因为是从大到小已经排过序了,出现过的数是一定要大于当前查询的这个数的。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200010;
int c[N];
struct node
{
    int w, idx;
} a[N];
int ans[N], num[N];
int n, k;
bool cmp(node a, node b)
{
    if (a.w != b.w)
        return a.w > b.w;
    else
        return a.idx > b.idx;
}
int lowbit(int x)
{
    return x & (-x);
}
void add(int x)
{
    for (; x <= n; c[x]++, x += lowbit(x))
        ;
}
int getsum(int x)
{
    int ans = 0;
    for (; x > 0; ans += c[x], x -= lowbit(x))
        ;
    return ans;
}
bool check(int x, int mid)
{
    return getsum(mid - 1) - getsum(x - 1) >= k;
    ;
}
void solve()
{
    scanf("%lld%lld", &n, &k);
    for (int i = 1; i <= n; i++)
    {

        scanf("%lld", &a[i].w);
        num[i] = a[i].w;
        c[i] = 0;
        a[i].idx = i;
    }
    sort(a + 1, a + 1 + n, cmp);
    for (int i = 1; i <= n; i++)
    {
        add(a[i].idx);
        if (getsum(a[i].idx - 1) < k)
        {
            ans[a[i].idx] = -1;
        }
        else
        {
            int l = 1, r = a[i].idx - 1;
            while (l < r)
            {
                int mid = l + r + 1 >> 1;
                if (check(mid, a[i].idx))
                    l = mid;
                else
                    r = mid - 1;
            }
            ans[a[i].idx] = num[l];
        }
    }
    for (int i = 1; i <= n; i++)
    {
        printf("%lld\n",ans[i]);
    }
}
signed main()
{
    int t;
    scanf("%lld", &t);
    while (t--)
        solve();
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值