[LibreOJ β Round]ZQC的手办

题目大意

区间对一个数取max。
区间求最小的x个比k小的数。

线段树

第一个操作很好搞。
第二个操作有个很显然的常数大做法。
实际上可以用堆把这个区间的笛卡尔树按优先级广搜。

#include<cstdio>
#include<algorithm>
#include<queue>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=500000+10,inf=1000000000;
struct dong{
    int x,y;
    friend bool operator <(dong a,dong b){
        return a.x<b.x||a.x==b.x&&a.y<b.y;
    }
    friend dong operator +(dong a,dong b){
        if (a<b) return a;else return b;
    }
} tmp;
struct suan{
    int l,r,p,v;
    friend bool operator <(suan a,suan b){
        return a.v>b.v||a.v==b.v&&a.p<b.p;
    }
} zlt;
priority_queue<suan> dl;
dong tree[maxn*4];
int mx[maxn*4],a[maxn],ans[maxn],sta[80];
bool bz[maxn*4];
int i,j,k,l,r,p,t,n,m,tot,top;
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<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void build(int p,int l,int r){
    if (l==r){
        tree[p].x=a[l];
        tree[p].y=l;
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    tree[p]=tree[p*2]+tree[p*2+1];
}
void mark(int p,int v){
    bz[p]=1;
    mx[p]=max(mx[p],v);
    if (tree[p].x<v) tree[p].x=v;
}
void down(int p){
    if (bz[p]){
        mark(p*2,mx[p]);
        mark(p*2+1,mx[p]);
        bz[p]=0;
    }
}
void change(int p,int l,int r,int a,int b,int v){
    if (l==a&&r==b){
        mark(p,v);
        return;
    }
    down(p);
    int mid=(l+r)/2;
    if (b<=mid) change(p*2,l,mid,a,b,v);
    else if (a>mid) change(p*2+1,mid+1,r,a,b,v);
    else change(p*2,l,mid,a,mid,v),change(p*2+1,mid+1,r,mid+1,b,v);
    tree[p]=tree[p*2]+tree[p*2+1];
}
void query(int p,int l,int r,int a,int b){
    if (l==a&&r==b){
        tmp=tmp+tree[p];
        return;
    }
    down(p);
    int mid=(l+r)/2;
    if (b<=mid) query(p*2,l,mid,a,b);
    else if (a>mid) query(p*2+1,mid+1,r,a,b);
    else query(p*2,l,mid,a,mid),query(p*2+1,mid+1,r,mid+1,b);
}
int main(){
    n=read();
    fo(i,1,n) a[i]=read();
    build(1,1,n);
    m=read();
    fo(i,1,m){
        t=read();
        if (t==1){
            l=read();r=read();k=read();
            change(1,1,n,l,r,k);
        }
        else{
            l=read();r=read();k=read();j=read();
            if (r-l+1<j){
                printf("-1\n");
                continue;
            }
            while (!dl.empty()) dl.pop();
            zlt.l=l;zlt.r=r;
            tmp.x=inf;
            query(1,1,n,l,r);
            zlt.p=tmp.y;zlt.v=tmp.x;
            dl.push(zlt);
            tot=0;
            while (j--){
                zlt=dl.top();
                dl.pop();
                if (zlt.v>=k) break;
                ans[++tot]=zlt.v;
                l=zlt.l;r=zlt.r;p=zlt.p;
                if (l<p){
                    zlt.l=l;
                    zlt.r=p-1;
                    tmp.x=inf;
                    query(1,1,n,l,p-1);
                    zlt.p=tmp.y;zlt.v=tmp.x;
                    dl.push(zlt);
                }
                if (p<r){
                    zlt.l=p+1;
                    zlt.r=r;
                    tmp.x=inf;
                    query(1,1,n,p+1,r);
                    zlt.p=tmp.y;zlt.v=tmp.x;
                    dl.push(zlt);
                }
            }
            if (j>=0) printf("-1\n");
            else{
                fo(j,1,tot) printf("%d ",ans[j]);
                printf("\n");
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值