1. 程式人生 > 實用技巧 >CF1398E Two Types of Spells(set+貪心)

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