K小数查询求k小数的非主席树做法

题目链接
题意
给出一个长度为 n 的数列 A,接下来有m 次操作,操作有两种:
1 l r x,表示对i属于[l,r],令Ai = min(Ai,x)
2 l r k,表示询问区间[l,r]中第k小的数。
思路
第k小的数,也就是说小于第k小的有k-1个,考虑二分加分块,分块询问的时候求的是小于mid的数有多少个,二分逼近答案。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 7;
typedef long long ll;
int belong[maxn], l[2005], r[2005], tag[2005], num, block, n, q;
vector<int> v[2005];
int a[maxn];
void build()
{
    block = sqrt(n);
    num = n / block;
    if(n % block) num++;
    for (int i = 1; i <= num; i++)
        l[i] = (i - 1) * block + 1, r[i] = i * block;
    r[num] = n;
    for (int i = 1; i <= n; i++) {
        belong[i] = (i - 1) / block + 1;
        v[belong[i]].push_back(a[i]);
    }
    for (int i = 1; i <= num; i++) {
        tag[i] = INF;
        sort(v[i].begin(), v[i].end());
    }
}
void reset(int x)
{
    v[x].clear();
    for (int i = l[x]; i <= r[x]; i++) {
        a[i] = min(a[i], tag[x]);
        v[x].push_back(a[i]);
    }
    sort(v[x].begin(), v[x].end());
}
void update(int x, int y, int c) {
    int tl = belong[x], tr = belong[y];
    if(tl == tr) {
        for (int i = x; i <= y; i++)
            a[i] = min(a[i], c);
        reset(tl);
        return ;
    }
    for (int i = x; i <= r[tl]; i++)
        a[i] = min(a[i], c);
    reset(tl);
    for (int i = l[tr]; i <= y; i++)
        a[i] = min(a[i], c);
    reset(tr);
    for (int i = tl + 1; i < tr; i++)
        tag[i] = min(tag[i], c);
}
int query(int x, int y, int c)
{
    int tl = belong[x], tr = belong[y], ans = 0;
    if(tl == tr) {
        for (int i = x; i <= y; i++) {
            if(a[i] <= c || tag[tl] <= c) ans++;
        }
        return ans;
    }
    for (int i = x; i <= r[tl]; i++) {
        if(a[i] <= c || tag[tl] <= c) ans++;
    }
    for (int i = l[tr]; i <= y; i++) {
        if(a[i] <= c || tag[tr] <= c) ans++;
    }
    for (int i = tl + 1; i < tr; i++) {
        if(tag[i] <= c) ans += (r[i] - l[i] + 1);
        else ans += upper_bound(v[i].begin(), v[i].end(), c) - v[i].begin();
    }
    return ans;
}
int check(int x, int y, int k) {
    int left = -1e9, right = 1e9;
    int ans;
    while (left <= right) {
        int mid = (left + right) / 2;
        if(query(x, y, mid) >= k) {
            ans = mid;
            right = mid - 1;
        }
        else left = mid + 1;
    }
    return ans;
}
int main()
{
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    build();
    for (int i = 1; i <= q; i++) {
        int op, x, y, z;
        scanf("%d%d%d%d", &op, &x, &y, &z);
        if(op == 1) update(x, y, z);
        else printf("%d\n", check(x, y, z));
    }
}

乘法
题意
给A,B两个数集,求ai*bi的第K大的值。
思路
求第K大问题,用二分询问比mid大的数有多少个。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
typedef long long ll;
ll a[maxn], b[maxn], k;
int n, m;
bool check(ll val) {
    ll cnt = 0;
    for (int i = 1; i <= n; i++) {
        if(a[i] == 0) cnt += val < 0 ? m : 0;
        else if(a[i] < 0) cnt += lower_bound(b + 1, b + 1 + m, ceil(1.0*val/a[i])) - b - 1;
        else cnt += (m - (upper_bound(b + 1, b + 1 + m, floor(1.0 * val / a[i])) - b - 1));
    }
    return cnt <= k;
}
int main()
{
    scanf("%d%d%lld", &n, &m, &k);
    k--;
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    for (int i = 1; i <= m; i++) scanf("%lld", &b[i]);
    sort(a + 1, a + 1 + n); sort(b + 1, b + 1 + m);
    ll left = -1e12, right = 1e12, ans;
    while (left <= right) {
        ll mid = (left + right) / 2;
        if(check(mid)) {
            ans = mid;
            right = mid - 1;
        }
        else left = mid + 1;
    }
    printf("%lld\n", ans);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值