题目链接: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;
}