题意:
解法:
先 考 虑 没 有 限 制 的 情 况 下 , 原 序 列 最 多 能 够 删 除 多 少 个 数 . 如 果 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]=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的位置我用的是树状数组.
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;
}