LibreOJ504 - ZQC 的手办 - 线段树、堆

ZQC 的手办

题目链接

分类:线段树、堆

1.题意概述

  • 给你一个序列a[1…n],有两种操作:
    • 1 l r k :表示把区间 [l,r] 中小于 k 的数都改成k
    • 2 l r k x :表示从小到大输出在区间 [l,r] 中小于 k 的数的前x个,如果不足则输出-1
  • 数据范围:
    • 1n5×105 :表示序列长度
    • 1m5×105 :表示操作次数
    • 1ai1091k1091x105

2.解题思路

  • 用一棵线段树维护区间最小值及其位置,这个可以在单次 O(logn) 时间内维护。
    每次查询时,用一个堆查询区间最小的 x 个值,具体做法是:用一个四元组(l,r,p,v)表示区间 [l,r] 内, p 这个位置的值最小,值为v,以 v 作为比较的关键字维护一个最小堆。初始时把整个询问区间的查询结果放入堆中,然后重复以下操作x次:

    • 取出堆顶元素并加入答案——这个答案肯定是前 x 小的
    • 如果lp,把 [l,p1] 的查询结果加入堆中
    • 如果 rp ,把 [p+1,r] 的查询结果加入堆中

    如果 vx 或者区间元素不足,则输出 1 ,否则按顺序输出即可!

  • 复杂度分析

    • 时间复杂度: O[(n+x)logn]
    • 空间复杂度: O(n)

3.AC代码

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define maxn 500010
#define lson root << 1
#define rson root << 1 | 1
#define lent (t[root].r - t[root].l + 1)
#define lenl (t[lson].r - t[lson].l + 1)
#define lenr (t[rson].r - t[rson].l + 1)
#define N 1111
#define eps 1e-6
#define pi acos(-1.0)
#define e exp(1.0)
#define Close() ios::sync_with_stdio(0),cin.tie(0)
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
typedef unsigned long long ull;
int a[maxn];
struct tree
{
    int l, r, val, tag, pos;
} t[maxn << 2];
struct data
{
    int l, r, v, pos;
    data(int x, int y, int c, int d) { l = x; r = y; v = c; pos = d; }
    friend bool operator< (const data &x, const data &y)
    {
        return x.v > y.v;
    }
};
priority_queue<data> q;
vector<int> ans;
void pushup(int root)
{
    if (t[lson].val < t[rson].val)
    {
        t[root].val = t[lson].val;
        t[root].pos = t[lson].pos;
    }
    else
    {
        t[root].val = t[rson].val;
        t[root].pos = t[rson].pos;
    }
}
void pushdown(int root)
{
    if (!t[root].tag || t[root].l == t[root].r)
        return;
    t[lson].val = max(t[lson].val, t[root].tag);
    t[rson].val = max(t[rson].val, t[root].tag);
    t[lson].tag = t[rson].tag = t[root].tag;
    t[root].tag = 0;
}
void build(int l, int r, int root)
{
    t[root].l = l;
    t[root].r = r;
    t[root].tag = 0;
    if (l == r)
    {
        t[root].pos = l;
        t[root].val = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(l, mid, lson);
    build(mid + 1, r, rson);
    pushup(root);
}
void update(int l, int r, int k, int root)
{
    if (l <= t[root].l && t[root].r <= r)
    {
        if (t[root].val < k)
            t[root].val = k;
        t[root].tag = k;
        return;
    }
    pushdown(root);
    int mid = t[root].l + t[root].r >> 1;
    if (l <= mid)
        update(l, r, k, lson);
    if (r > mid)
        update(l, r, k, rson);
    pushup(root);
}
pair<int, int> query(int l, int r, int root)
{
    if (l <= t[root].l && t[root].r <= r)
        return make_pair(t[root].val, t[root].pos);
    pushdown(root);
    int mid = t[root].l + t[root].r >> 1;
    pair<int, int> res = make_pair(1e9, 0);
    if (l <= mid)
        res = min(res, query(l, r, lson));
    if (r > mid)
        res = min(res, query(l, r, rson));
    return res;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    long _begin_time = clock();
#endif
    int n, m;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    build(1, n, 1);
    scanf("%d", &m);
    while (m--)
    {
        int op, l, r, k, x;
        scanf("%d%d%d%d", &op, &l, &r, &k);
        if (op == 1)
            update(l, r, k, 1);
        else
        {
            scanf("%d", &x);
            if (r - l + 1 < x)
            {
                puts("-1");
                continue;
            }
            while (!q.empty())
                q.pop();
            ans.clear();
            pair<int, int> u = query(l, r, 1);
            q.push(data(l, r, u.first, u.second));
            for (int i = 1; i <= x; i++)
            {
                data u = q.top();
                q.pop();
                if (u.v < k)
                    ans.push_back(u.v);
                else break;
                if (u.l < u.pos)
                {
                    pair<int, int> w = query(u.l, u.pos - 1, 1);
                    q.push(data(u.l, u.pos - 1, w.first, w.second));
                }
                if (u.r > u.pos)
                {
                    pair<int, int> w = query(u.pos + 1, u.r, 1);
                    q.push(data(u.pos + 1, u.r, w.first, w.second));
                }
            }
            int sz = ans.size();
            if (sz < x)
                puts("-1");
            else
            {
                for (int i = 0; i < sz; i++)
                    if (i == 0)
                        printf("%d", ans[i]);
                    else
                        printf(" %d", ans[i]);
                puts("");
            }
        }
    }
#ifndef ONLINE_JUDGE
    long _end_time = clock();
    printf("time = %ld ms.", _end_time - _begin_time);
#endif
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值