E

题: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;
}
View Code
原文地址:https://www.cnblogs.com/starve/p/13632285.html