CF1398E Two Types of Spells(set+贪心)

主要是思想是对顶堆,将最大的雷电法术的放入加强集合,其他放入普通集合

之后维护两种法术的集合大小,因为每次操作最多只会将大小与真正的大小差1,因此判断更新即可

具体注释看代码,细节比较多,主要是注意判断集合是否非空

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
typedef pair<int,pll> plll;
const int N=1e6+10;
set<int> light,fire,s,od;
ll tmp,res;
//维护四个集合,分别是light法术、fire法术、被加强的法术、没被加强的法术
void del(int d,int opt){
    d=-d;
    res-=d;
    if(opt==1) light.erase(d);
    else fire.erase(d);
    if(s.count(d))
        tmp-=d,s.erase(d);
    else
        od.erase(d);
}
void add(int d,int opt){
    res+=d;
    if(opt==1) light.insert(d);
    else fire.insert(d);
    //当普通的为空,或者当前值大于最大的普通值,就加入加强的集合
    if(od.empty()||d>*prev(od.end()))
        tmp+=d,s.insert(d);
    else
        od.insert(d);
}
void maintain(){
    //当被加强的多了,就要删除最小的
    if((int)s.size()>(int)light.size()){
        tmp-=*s.begin();
        od.insert(*s.begin());
        s.erase(s.begin());
    }
    //当小于,就要加上
    if((int)s.size()<(int)light.size()){
        s.insert(*prev(od.end()));
        tmp+=*prev(od.end());
        od.erase(prev(od.end()));
    }
}
int main(){
    ios::sync_with_stdio(false);
    int n;
    cin>>n;
    int i;
    for(i=1;i<=n;i++){
        int opt;
        int d;
        cin>>opt>>d;//插入或者删除
        if(d<0) del(d,opt);
        else add(d,opt);
        maintain();//维护状态
        if(od.empty()&&s.empty()){
            cout<<0<<endl;
        }
        else if(fire.empty()||(!light.empty()&&*light.begin()>=*prev(fire.end()))){
            //两种情况要特殊讨论
            //对于一般情况都是有light长度的能被加强,但是当全部法术都是light或者最大light长度属于light的话
            //第一种只有light-1的被加强,第二种是非法的,因为必须要存在一个fire被加强
            //因此策略就是最小的light不能double,如果存在fire,取最大的
            ll ans=tmp+res-*light.begin();
            if(fire.size())
                ans+=*prev(fire.end());
            cout<<ans<<endl;
        }
        else{
            cout<<tmp+res<<endl;
        }
    }
    return 0;
}
View Code

 

posted @ 2020-08-23 15:22  朝暮不思  阅读(173)  评论(0编辑  收藏  举报