LibreOJ #504. 「LibreOJ β Round」ZQC 的手办 线段树+堆

题意

给出一个序列,要求资瓷两个操作:
1 l r k表示把[l,r]中小于k的数都改成k
2 l r k x表示从小到大输出[l,r]中小于k的x个数,若数量不足则输出-1
n,q<=500000

分析

比赛的时候想到了分块做法,也就是对每块维护一个有序的数组,然后修改的话整块打标记,多出来的O(nlogn)暴力修改。查询的话把每块最小的那个和多出来的都扔进堆里面,然后每次找到堆顶再把其对应块中该元素的后缀仍进去就好了。
但略微算了一下,发现复杂度较高,于是不敢打。

正解:
考虑用线段树维护区间最小值和其所在位置。查询的话,用一个堆维护四元组(l,r,v,pos)。首先把整个区间(l,r,[l,r]最小值,所在位置)扔进堆中。每次取堆顶,若l < pos则加入(l,pos-1,v’,pos’);若r>pos则加入(pos+1,r,v’,pos’)。
时间复杂度O(nlog2n)

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;

const int N=500005;

int n,m,a[N];
struct tree{
  int mn,tag,pos;}t[N*5];
struct data
{
    int l,r,v,pos;

    bool operator < (const data &a) const
    {
        return v>a.v;
    }
};
priority_queue<data> q;
vector<int> ans;

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){
  if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<=
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值