【思维】线段树维护后缀和+set——cf1373G

感觉是很不错的题

很关键的一步是题意简化:有m个操作,每次操作可以在数组某点+1或-1,问每次操作后,最大的 suf[i]-(n-i+1)是多少

显然对pos进行修改,会影响到[1,pos]整个区间的后缀和。所以线段树区间更新就可以。

ps:原题意下这题细节较多,要将数组扩充到2n,并且用一个multiset维护受影响的最右端

#include<bits/stdc++.h>
using namespace std;
#define N 600005
#define mk make_pair

int n,k,m,nn;
set<pair<int,int> >s;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int mx[N<<2],lazy[N<<2];
void build(int l,int r,int rt){
    if(l==r){
        mx[rt]=l-n-1;return;
    } 
    int m=l+r>>1;
    build(lson);build(rson);
    mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
} 
void pushdown(int rt){
    if(lazy[rt]){
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        mx[rt<<1]+=lazy[rt];
        mx[rt<<1|1]+=lazy[rt];
        lazy[rt]=0;
    }
}
void update(int L,int R,int v,int l,int r,int rt){
    if(L<=l && R>=r){
        lazy[rt]+=v;mx[rt]+=v;return;
    }
    pushdown(rt);
    int m=l+r>>1;
    if(L<=m)update(L,R,v,lson);
    if(R>m)update(L,R,v,rson);
    mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
}
int query(int L,int R,int l,int r,int rt){
    if(L<=l && R>=r)return max(mx[rt],0);
    pushdown(rt);
    int m=l+r>>1,res=0;
    if(L<=m)res=max(res,query(L,R,lson));
    if(R>m)res=max(res,query(L,R,rson));
    return res;
}

multiset<int>ss;
int main(){
    cin>>n>>k>>m;nn=n*2;build(1,nn,1);
    while(m--){
        int x,y;scanf("%d%d",&x,&y);
        if(s.find(mk(x,y))!=s.end()){//移除这个点 
            s.erase(mk(x,y)); 
            int pos=abs(k-x)+y;
            update(1,pos,-1,1,nn,1);
            ss.erase(ss.find(pos));    
            if(s.size()==0){
                cout<<0<<'\n';
                continue;
            }
            auto p=ss.end();p--;
            cout<<query(1,*p,1,nn,1)<<'\n';
        }else {//增加这个点 
            s.insert(mk(x,y));
            int pos=abs(k-x)+y;
            update(1,pos,1,1,nn,1);
            ss.insert(pos);
            auto p=ss.end();p--;
            cout<<query(1,*p,1,nn,1)<<'\n';
        }
    }
}

 

posted on 2020-07-11 19:22  zsben  阅读(180)  评论(0编辑  收藏  举报

导航