peiwenjun's blog 没有知识的荒原

CF1326E Bombs 题解

题目描述

给定排列 \(p_i,q_i\) ,在数轴的第 \(i\) 个位置上摆放了一个数 \(p_i\)

现在依次加入炸弹,第 \(i\) 次在 \(q_i+0.5\) 的位置加入一个炸弹,然后从左往右扫描,扫到第 \(i\) 个位置时把 \(p_i\) 加入大根堆中,扫到一个炸弹就 \(\texttt{pop}\) 掉堆顶。

在每一次加入炸弹前,求最终堆顶元素。

数据范围

  • \(1\le n\le 3\cdot 10^5\)
  • \(1\le p_i,q_i\le n\) ,保证 \(p,q\) 为排列。

时间限制 \(\texttt{3s}\) ,空间限制 \(\texttt{256MB}\)

分析

容易发现加入炸弹的过程中,最终堆顶元素会不断变小。

双指针,问题转化为已知加入了若干炸弹,询问堆顶能否 \(\ge x\)

\(p_i\ge x\) 的位置全部拿出来,如果堆顶 \(<x\) ,那么每个 \(p_i\) 都有一个炸弹与之匹配。

换言之,如果把 \(p_i\ge x\) 的位置看成单点 \(+1\) ,把炸弹看成单点 \(-1\) ,那么堆顶 \(\lt x\) 当且仅当任意一个后缀权值和都 \(\le 0\)

单点加、查询全局后缀 \(\max\) ,转化成前缀加、查询全局 \(\max\) ,线段树维护即可。

时间复杂度 \(\mathcal O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=500005;
int n,pos[maxn];
int read()
{
    int q=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=10*q+ch-'0',ch=getchar();
    return q;
}
struct node
{
    int l,r,add,mx;
}f[4*maxn];
void pushadd(int p,int v)
{
    f[p].add+=v,f[p].mx+=v;
}
void pushdown(int p)
{
    if(f[p].add==0) return ;
    pushadd(2*p,f[p].add),pushadd(2*p+1,f[p].add);
    f[p].add=0;
}
void pushup(int p)
{
    f[p].mx=max(f[2*p].mx,f[2*p+1].mx);
}
void build(int p,int l,int r)
{
    f[p].l=l,f[p].r=r;
    if(l==r) return ;
    int mid=(l+r)/2;
    build(2*p,l,mid);
    build(2*p+1,mid+1,r);
    pushup(p);
}
void modify(int p,int l,int r,int v)
{
    if(l<=f[p].l&&f[p].r<=r)
    {
        pushadd(p,v);
        return ;
    }
    if(l>f[p].r||r<f[p].l) return ;
    pushdown(p);
    modify(2*p,l,r,v);
    modify(2*p+1,l,r,v);
    pushup(p);
}
int main()
{
    build(1,1,n=read());
    for(int i=1;i<=n;i++) pos[read()]=i;
    modify(1,1,pos[n],1),printf("%d ",n);
    for(int i=1,j=n;i<n;i++)
    {
        modify(1,1,read(),-1);
        while(j&&f[1].mx<=0) modify(1,1,pos[--j],1);
        printf("%d ",j);
    }
    return 0;
}

posted on 2022-04-08 10:04  peiwenjun  阅读(12)  评论(0)    收藏  举报

导航