CF1326E Bombs

E. Bombs

题意:给定一个排列,和 n 个地雷,按顺序将排列放入集合,遇到地雷则删去集合最大值,求地雷个数为 i 个时,集合最终的最大值为多少。

分析:首先可以看出答案一定是一个非递增序列,考虑类似二分的操作。对于每个节点 i ,若 i 点之后 >= pi 的数量小于等于后面地雷的数量,那 pi 一定会被炸掉,但这样不好维护。依据这点,我们可以尝试枚举答案 x ,每个位置记录 ( >=x的数量 - 地雷的数量) ,当所有的位置的值都 <=0 时,说明答案能取。

题解:按照分析写,区间维护和全局最大值用线段树就行,在缩小答案和增加地雷时,都是前缀的形式+1 -1,因为我们统计的是每个位置之后的数量,那么对于修改时在当前位置之后的结果是没有影响的。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 10;
ll mod=998244353;
ll n,tot,m;
int a[N],c[N],mp[N];
int tr[N<<2],tag[N<<2];
void push_up(int now){
    tr[now]=max(tr[now<<1],tr[now<<1|1]);
}
void push_down(int now,int l,int r){
    int mid=(l+r)/2;
    tag[now<<1]+=tag[now];
    tr[now<<1]+=tag[now];
    tag[now<<1|1]+=tag[now];
    tr[now<<1|1]+=tag[now];
    tag[now]=0;
}
void update(int now,int nl,int nr,int l,int r,int v){
    if(l>=nl&&r<=nr) {tr[now]+=v;tag[now]+=v;return;}
    push_down(now,l,r);
    int mid=(l+r)/2;
    if(mid>=nl) update(now<<1,nl,nr,l,mid,v);
    if(mid+1<=nr) update(now<<1|1,nl,nr,mid,r,v);
    push_up(now);
}
int main() {
//    freopen("../in.in", "r", stdin);
//    ios::sync_with_stdio(false);
    ll k, _;
//    cin>>_;
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),mp[a[i]]=i;
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    int ans=n;
    update(1,1,mp[ans],1,n,1);
    for(int i=1;i<n;i++){
        printf("%d ",ans);
        update(1,1,c[i],1,n,-1);
        while(tr[1]<=0){
            ans--;
            update(1,1,mp[ans],1,n,1);
        }
    }
    printf("%d\n",ans);
    return 0;
}
View Code

 

posted @ 2021-02-11 16:11  Cc0  阅读(134)  评论(0编辑  收藏  举报