题:https://codeforces.com/problemset/problem/1404/C
题意:给定n个数的序列,每次可将下标==权值的位置remove,剩下的俩个区间则合并在一起。q个询问区间问[l+1,n-r]最多能删去多少个。
分析:前面的点对后面的位置有影响,所以先将查询离线起来按r来排序,查询就以枚举的r为右边界来记录答案;
操作ai=i-ai,remove就是删除ai==0的位置,定义f(l ,r)为区间[l,r]最多能删除多少个数;
对于当前枚举的r,若a[i]>=0,找到最大的l:lmax满足f(lmax,r)>=a[i],然后就在[1,lmax]+1,代表若查询区间的右端点r,左端点在[1,lmax]内的答案贡献加1(因为依据二分查询的[lmax,r]区间内已经有a[i]个数可remove来使a[i]能remove);
而f(l,r)则用BIT维护,可以保证f(1,r),f(2,r),f(3,r),...为单调不降的。
#include<bits/stdc++.h> using namespace std; #define pb push_back #define MP make_pair #define pii pair<int,int> typedef long long ll; const int mod=1e9+7; const int M=1e6+6; const int inf=0x3f3f3f3f; const ll INF=1e18; int a[M],ans[M],n,q; vector<pii >v[M]; struct BIT{ int tr[M]; void init(){ memset(tr,0,sizeof(tr)); } void add(int i,int x){ while(i<=n) tr[i]+=x,i+=i&(-i); } int sum(int i){ int res=0; while(i){ res+=tr[i]; i-=i&(-i); } return res; } }t1; int main(){ scanf("%d%d",&n,&q); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); a[i]=i-a[i]; } for(int i=1;i<=q;i++){ int l,r; scanf("%d%d",&l,&r); l=l+1,r=n-r; v[r].pb(MP(l,i)); } t1.init(); for(int i=1;i<=n;i++){ if(a[i]>=0){ int l=1,r=i,pos=0; while(l<=r){ int midd=(l+r)>>1; if(t1.sum(midd)>=a[i]){ l=midd+1; pos=midd; } else r=midd-1; } if(pos){ t1.add(1,1); t1.add(pos+1,-1); } } for(auto it:v[i]){ int l=it.first,id=it.second; ans[id]=t1.sum(l); } } for(int i=1;i<=q;i++) printf("%d ",ans[i]); return 0; }