Codeforces1404 C. Fixed Point Removal(离线,线段树,树状数组)

题意:

在这里插入图片描述

解法:

先 考 虑 没 有 限 制 的 情 况 下 , 原 序 列 最 多 能 够 删 除 多 少 个 数 . 如 果 a [ i ] < i , 如 果 使 得 a [ i ] = i , 左 边 还 需 要 删 除 b [ i ] = i − a [ i ] 个 数 . 如 果 a [ i ] > i , 那 么 这 个 数 一 定 不 会 被 删 除 , 那 么 令 b [ i ] = i n f . 观 察 到 每 次 删 除 最 右 边 可 删 除 的 数 , 一 定 是 最 优 的 , 线 段 树 维 护 b [ ] , 每 次 找 到 最 右 边 的 满 足 b [ x ] = 0 的 位 置 x , 将 x 位 置 删 除 ( b [ x ] = i n f ) , 然 后 b [ x + 1 , n ] − = 1 , 一 直 操 作 到 无 法 继 续 为 止 , 由 于 最 多 删 除 n 个 位 置 , 因 此 复 杂 度 O ( n ∗ l o g ) . − − − 如 果 只 有 多 组 y 限 制 查 询 , 如 何 计 算 答 案 : 标 记 所 有 可 以 删 除 的 数 的 位 置 , 统 计 有 多 少 个 位 置 < = n − y 即 可 . − − − 如 果 只 有 多 组 x 限 制 查 询 , 如 何 计 算 答 案 : 因 为 每 次 都 是 操 作 右 边 的 数 , 将 询 问 离 线 , 按 x 从 大 到 小 排 序 , 每 次 操 作 x 右 边 的 数 即 可 . − − − 现 在 考 虑 既 有 x 限 制 又 有 y 限 制 的 查 询 , 如 何 计 算 答 案 : 其 实 就 是 在 x 限 制 的 情 况 下 , 记 录 有 多 少 个 位 置 已 经 被 删 除 , 然 后 查 询 < = n − y 的 有 多 少 个 即 可 . − − − 可 以 线 段 树 维 护 b [ ] , 因 为 需 要 支 持 区 间 减 法 操 作 , 查 询 < = n − y 的 位 置 我 用 的 是 树 状 数 组 . 先考虑没有限制的情况下,原序列最多能够删除多少个数.\\ 如果a[i]<i,如果使得a[i]=i,左边还需要删除b[i]=i-a[i]个数.\\ 如果a[i]>i,那么这个数一定不会被删除,那么令b[i]=inf.\\ 观察到每次删除最右边可删除的数,一定是最优的,\\ 线段树维护b[],每次找到最右边的满足b[x]=0的位置x,\\ 将x位置删除(b[x]=inf),然后b[x+1,n]-=1,\\ 一直操作到无法继续为止,由于最多删除n个位置,因此复杂度O(n*log).\\ ---\\ 如果只有多组y限制查询,如何计算答案:\\ 标记所有可以删除的数的位置,统计有多少个位置<=n-y即可.\\ ---\\ 如果只有多组x限制查询,如何计算答案:\\ 因为每次都是操作右边的数,将询问离线,按x从大到小排序,\\ 每次操作x右边的数即可.\\ ---\\ 现在考虑既有x限制又有y限制的查询,如何计算答案:\\ 其实就是在x限制的情况下,记录有多少个位置已经被删除,\\ 然后查询<=n-y的有多少个即可.\\ ---\\ 可以线段树维护b[],因为需要支持区间减法操作,\\ 查询<=n-y的位置我用的是树状数组. ,.a[i]<i,使a[i]=i,b[i]=ia[i].a[i]>i,,b[i]=inf.,,线b[],b[x]=0x,x(b[x]=inf),b[x+1,n]=1,,n,O(nlog).y,:,<=ny.x,:,线,x,x.xy,:x,,<=ny.线b[],,<=ny.

code:
#include <bits/stdc++.h>
using namespace std;
const int maxm=1e6+5;
struct QQ{
    int x,y,id;
    bool operator<(const QQ& a)const{
        return x>a.x;
    }
}Q[maxm];
int ans[maxm];
int a[maxm];
int n,q;
struct Tree{
    int v[maxm<<2];
    int laz[maxm<<2];
    inline void pp(int node){
        v[node]=min(v[node*2],v[node*2+1]);
    }
    inline void pd(int node){
        if(laz[node]){
            v[node*2]+=laz[node];v[node*2+1]+=laz[node];
            laz[node*2]+=laz[node];laz[node*2+1]+=laz[node];
            laz[node]=0;
        }
    }
    void update(int x,int val,int l,int r,int node){
        if(l==r){v[node]=val;return ;}
        pd(node);int mid=(l+r)/2;
        if(x<=mid)update(x,val,l,mid,node*2);
        else update(x,val,mid+1,r,node*2+1);
        pp(node);
    }
    void add(int st,int ed,int val,int l,int r,int node){
        if(st<=l&&ed>=r){v[node]+=val;laz[node]+=val;return ;}
        pd(node);int mid=(l+r)/2;
        if(st<=mid)add(st,ed,val,l,mid,node*2);
        if(ed>mid)add(st,ed,val,mid+1,r,node*2+1);
        pp(node);
    }
    int ask(int x,int l,int r,int node){
        if(v[node]>0||r<x)return 1e9;
        if(l==r)return l;
        pd(node);int mid=(l+r)/2;int ans=1e9;
        ans=ask(x,mid+1,r,node*2+1);//优先找右边
        if(ans==1e9)ans=ask(x,l,mid,node*2);
        pp(node);return ans;
    }
    void build(int l,int r,int node){
        v[node]=1e9;laz[node]=0;
        if(l==r)return ;
        int mid=(l+r)/2;build(l,mid,node*2);build(mid+1,r,node*2+1);
    }
}T;
struct BIT{
    int c[maxm];
    int lowbit(int i){return i&-i;}
    void add(int i,int t){while(i<maxm)c[i]+=t,i+=lowbit(i);}
    int ask(int i){int ans=0;while(i)ans+=c[i],i-=lowbit(i);return ans;}
}BT;
void solve(){
    cin>>n>>q;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=q;i++)cin>>Q[i].x>>Q[i].y,Q[i].id=i;
    sort(Q+1,Q+1+q);//将询问按x从大到小排序.
    T.build(1,n,1);
    int j=n;
    for(int i=1;i<=q;i++){
        while(j>=1&&j>Q[i].x){//计算>=x的有多少个位置可以删除.
            if(j-a[j]>=0)T.update(j,j-a[j],1,n,1);
            while(T.v[1]==0){//如果有可以删除的数.
                int pos=T.ask(j,1,n,1);
                T.update(pos,1e9,1,n,1);
                if(pos+1<=n)T.add(pos+1,n,-1,1,n,1);
                BT.add(pos,1);
            }
            j--;
        }
        ans[Q[i].id]=BT.ask(n-Q[i].y);
    }
    for(int i=1;i<=q;i++)cout<<ans[i]<<endl;
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0);
    solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值