CF #668 (Div. 2)E. Fixed Point Removal(线段树)

题目链接:https://codeforces.com/contest/1405/problem/E
题意:有n个整数,下标从1~n,当元素的值与其下标值相同时,可将这个元素移除。问:把数列前若干个和后若干个元素变成n+1后,最多可以移除多少个元素。

思路:
将数值元素a[i]都替换成i-a[i],那么:①当a[i]=0时,就可以将原来下标为i的数移除。②若a[i]>0,那么还需要移走a[i]个前面的数,才能将原来下标为i的数移除。③若a[i]<0,就不能移除这个数。

令f[i]表示,1~i中能移除最多元素的个数,那么当f[i-1]>=a[i]时,就可以将原来下标为i的数移除,且f[i]=f[i-1]+1。所以我们可以从f[i-1] O(1)推出f[i]的值

然后我们可以离线处理,先将所有区间按右端点排序。令dp[i]表示,l=i时,[l,r]能移除的最多数的个数,维护r分别等于1,2,3,,,时数组dp的值(每次维护需要在[1,r]区间中 >=a[i]的dp值上+1,因为dp是一个递减的序列,所以每次dp中需要修改的地方肯定是一个前缀区间—>线段树查询、区间修改),当有右端点值等于当前r值时,查询答案。

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<string>
#include<vector>
using namespace std;
#define LL long long
#define uLL unsigned long long
#define PII pair<int,int>
#define mid ((l + r)>>1)
#define chl (root<<1)
#define chr (root<<1|1)
const int manx=3e5+10;
const int INF=0x3fffffff;

int a[manx],ans;
PII seg[manx],seg1[manx];
map<PII,int>mp;
struct node
{
    int mmin,lazy;
}tree[manx<<2];
void eval(int root)
{
    tree[root].mmin=min(tree[chl].mmin,tree[chr].mmin);
}
void build_tree(int root,int l,int r)
{
    tree[root].lazy=0;
    tree[root].mmin=0;
    if(l==r){
        tree[root].mmin=0;
        return;
    }
    build_tree(chl,l,mid);
    build_tree(chr,mid+1,r);
    eval(root);
}
void push_down(int root,int l,int r)
{
    if(tree[root].lazy==0)
        return;
    tree[chl].mmin+=tree[root].lazy;
    tree[chr].mmin+=tree[root].lazy;
    tree[chl].lazy+=tree[root].lazy;
    tree[chr].lazy+=tree[root].lazy;
    tree[root].lazy=0;

}
void change(int root,int l,int r,int ll,int rr,int val)//ll~rr区间范围内的数 +1
{
    if(l==ll&&r==rr){
        tree[root].mmin+=val;
        tree[root].lazy+=val;
        return;
    }
    push_down(root,l,r);
    if(mid>=rr)
        change(chl,l,mid,ll,rr,val);
    else if(mid<ll)
        change(chr,mid+1,r,ll,rr,val);
    else{
        change(chl,l,mid,ll,mid,val);
        change(chr,mid+1,r,mid+1,rr,val);
    }
    eval(root);
}
int query_pos(int root,int l,int r,int ll,int rr,int val)//小于val的第一个数
{
    if(l==r&&tree[root].mmin<val)//!
        return l;
    push_down(root,l,r);
    if(ll<=mid&&tree[chl].mmin<val)//如果左儿子的值小于val,直接只遍历左边
        return query_pos(chl,l,mid,ll,rr,val);
    else if(rr>mid&&tree[chr].mmin<val)//如果左儿子的值大于val,右儿子的值小于val,遍历右边
        return query_pos(chr,mid+1,r,ll,rr,val);
    else return -1;
}
int query(int root,int l,int r,int pos)
{
    if(l==r){
        return tree[root].mmin;
    }
    push_down(root,l,r);
    if(pos<=mid)
        return query(chl,l,mid,pos);
    else return query(chr,mid+1,r,pos);
}
int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[i]=i-a[i];
        if(a[i]<0)a[i]=INF;//小于0就不可能移走
    }
    for(int i=1;i<=q;i++){
        scanf("%d%d",&seg[i].first,&seg[i].second);
        ++seg[i].first;
        seg[i].second=n-seg[i].second;
        seg1[i].first=seg[i].first,seg1[i].second=seg[i].second;
    }
    build_tree(1,1,n);
    sort(seg+1,seg+q+1,[](PII a,PII b){return a.second<b.second;});
    int j=1;
    for(int i=1;i<=n;i++){
        int pos=query_pos(1,1,n,1,i,a[i]);//1~i中值小于a[i]的第一个数
        if(pos==-1)//都大于a[i]
            pos=i+1;
        if(pos>1)
            change(1,1,n,1,pos-1,1);//1~i中值大于a[i]的数 +1
        while(j<=q&&seg[j].second==i){
            ans=query(1,1,n,seg[j].first);
            mp[seg[j]]=ans;
            j++;
        }
    }
    for(int i=1;i<=q;i++)
        printf("%d\n",mp[seg1[i]]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值