[Codeforces Round #668 (Div. 2)]E. Fixed Point Removal

Codeforces Round #668 (Div. 2)E. Fixed Point Removal ##

题意

给你一个序列a,若 a [ i ] = = i a[i] == i a[i]==i你可以删除 a [ i ] a[i] a[i],删除后前后两部分将重新连接,序列a的权值为他能最多能删除的次数

给你q个查询,每个查询给你个x,y,表示将前x个元素和后个元素变为n+1,a的权值变为了多少

思路

首先我们对原序列分析,要是删除的次数最多,我们肯定是优先删除i大的,口胡证明若a,b位置可删除,a<b,先删除a的会影响a到n的可操作数,若该操作使得b到n的操作数增加,先操作b也能达到效果,但先操作a显然b位置将不可再操作,则总操作数将减小。

现在我们考虑如何按顺序求出原序列的删除位置。按照上面的贪心思路,用个线段树什么的就能求出,这里我用的是分块,效果差不多。

求出位置序列,假设为13 5 2 8 7 4 9 ,一个显然的结论是如果位置p被改变了的话,则在位置序列中p的后面的位置q,若 q > p q>p q>p则显然不会被操作,我们考虑前x个中在位置序列最前面的位置,假设x取6,那么最前面的就是5,根据上面的结论,5后面的操作都不能实现,若后面的操作q>5(前面的结论),q<5则被改变,所有对前缀我们维护下前面的最左边的位置即可。那么对后y个,我们直接算位置序列中前x的最左边位置的左边有多少个大于 n − y n-y ny即可。离线bit维护下即可。

代码

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define pb push_back
#define lowbit(x) ((x)&(-x))
#define inf 0x3f3f3f3f
#define endl "\n"
#define MP(x,y) (make_pair(x,y))
#define pii pair<int,int>
#define pll pair<long long,long long>
using namespace std;
int n,q;
int a[300005];
int mn[300005];
int id[300005];
int tag[300005];
int sz;
int ans[300005],tot;
int c[300005];
int pre[300005];
int ps[300005],p[300005];
void add(int x){
    if(x==0 || x>n) return;
    for(;x<=n;x+=lowbit(x)) c[x]++;
}
int getsum(int x){
    int res = 0;
    if(x>n) x = n;
    for(;x>0;x-=lowbit(x)) res+=c[x];
    return res;
}
struct query{
    int l,r,id;
    bool operator < (const query b) const{
        return r<b.r;
    }
}ask[300005];
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    memset(ps,inf,sizeof(ps));
    sz = sqrt(n);
    int mx = n/sz;
    memset(mn,0x3f,sizeof(mn));
    for(int i=1;i<=n;i++){
        id[i] = i/sz;
        a[i] = i-a[i];
        if(a[i]<0) a[i] = inf;
        mn[id[i]] = min(mn[id[i]],a[i]);
    }
    while(1){
        bool flag = 0;
        int pos = -1;
        for(int i = mx;i>=0;i--){
            if(flag) break;
            if(mn[i] -tag[i] <= 0){
                mn[i] = inf;
                for(int j = max(i*sz,1);j<min((i+1)*sz,n+1);j++){
                    a[j] -= tag[i];
                    if(a[j]<0) a[j] = inf;
                    if(a[j] == 0) pos = j;
                }
                if(pos!=-1){
                    flag = 1;
                    ans[tot++] = pos;
                    ps[pos] = tot;
                    a[pos] = inf;
                    for(int j = i+1;j<=mx;j++) tag[j]++;
                    for(int j=pos+1;j<min((i+1)*sz,n+1);j++){
                        a[j]--;
                        if(a[j]<0) a[j] = inf;
                    }
                }
                for(int j = i*sz;j<min((i+1)*sz,n+1);j++)
                    mn[i] = min(mn[i],a[j]);
                tag[i] = 0;
            }
        }
        if(!flag) break;
    }

    memset(p,0x3f,sizeof(p));
    for(int i=1;i<=n;i++){
        pre[i]=max(pre[i-1],tot-ps[i]+1);
        p[i] = min(p[i-1],ps[i]);
        if(p[i] == inf) p[i] = n+1;
    }
    for(int i=0;i<q;i++){
        scanf("%d%d",&ask[i].l,&ask[i].r);
        ask[i].id = i;
    }
    sort(ask,ask+q);

    int now = n;
    for(int i=0;i<q;i++){
        while(now>=n-ask[i].r+1){
            add(ps[now--]);
        }
        ans[ask[i].id] = tot-pre[ask[i].l]-getsum(p[ask[i].l]-1);
    }
    for(int i=0;i<q;i++){
        printf("%d\n",ans[i]);
    }
}

写的有点难看,主要是昨晚不太清醒的时候写的,当时有个问题始终没有解决,早上起来就会了,差点ak div2可惜了错过上大分的机会。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值