题解 C F 1398 E \mathrm{CF1398E} CF1398E
题目意思
S o l \mathrm{Sol} Sol
感觉这是最近我做过比较难的 E E E 啦,细节真的草鸡多
我们在这里假定 A A A 为能翻倍的法术, B B B 相反
我们首先有个贪心思路就是每个 A A A 尽量配对大的 权值,并且第一个使用 A A A 的本身权值尽可能小,因为其不能产生翻倍贡献。
我们考虑用 set 维护 A A A 权值的集合(既保证不重复也从小到大排序便于操作)。并且假设现在有 s s s 个 A A A 那么我们计 s 1 s1 s1 表示前 s s s 大的权值和, s 2 s2 s2 表示前 s + 1 s+1 s+1 大的权值和,以及 S S S 为当前所有权值的总和。
于是我们分类讨论:
-
假设当前 A A A 的最小值 m i mi mi 在所有权值的排名 ≤ s \leq s ≤s 那么贡献即为 ( S + p r e 2 − m i ) (S+pre2-mi) (S+pre2−mi)。具体地就是把这个最小的作为 A A A 的开头然后 s s s 个 A A A 本身连接使用,最后带一个 B B B。
-
对于其他情况的贡献即为 ( S + p r e 1 ) (S+pre1) (S+pre1) 即一个配一个即可。
对于权值的排名我们直接平衡树维护一下就好了(div2E 就要用平衡树/fad,直接贺了个fhq就好了)
时间复杂度: O ( n log n ) O(n\log n) O(nlogn)
C o d e \mathrm{Code} Code
const int N=1e6+5;
int n,m,noden,tot,cnt,pre1,pre2;
int ans,sum;
set<int> S;
struct node
{
int lc,rc,rnd,val,sz;
}a[N];
struct fhq{
#define new NEW
int rt;
void pushup(int x){
a[x].sz=a[a[x].lc].sz+a[a[x].rc].sz+1;
}
int new(int val){
a[++noden]=(node){
0,0,rand(),val,1};
return noden;
}
void split(int now,int val,int &x,int &y){
if(!now){
x=y=0;
return ;
}
if(a[now].val>=val){
x=now;
split(a[now].rc,val,a[now].rc,y);
}
else{
y=now;
split(a[now].lc,val,x,a[now].lc);
}
pushup(now);
}
int merge(int x,int y){
if(!x||!y) return x|y;
if(a[x].rnd<a[y].rnd){
a[x].rc=merge(a[x].rc,y);
pushup(x);
return x;
}
else{
a[y].lc=merge(x,a[y].lc);
pushup(y);
return y;
}
}
int getk(int x,int k){
if(!k||k>a[rt].sz) return 0;
for(;;){
if(k<=a[a[x].lc].sz) x=a[x].lc;
else if(k==a[a[x].lc].sz+1) return x;
else{
k-=a[a[x].lc].sz+1;
x=a[x].rc;
}
}
}
void insert(int val){
int x,y;
split(rt,val,x,y);
rt=merge(merge(x,new(val)),y);
}
void Del(int val){
int x,y,z;
split(rt,val,x,z);
split(x,val+1,x,y);
y=merge(a[y].lc,a[y].rc);
rt=merge(merge(x,y),z);
}
int Find(int val){
int res,x,y;
split(rt,val+1,x,y);
res=a[x].sz+1;
rt=merge(x,y);
return res;
}
int Kth(int rank){
return a[getk(rt,rank)].val;
}
}T;
signed main()
{
io.read(n);
for (;n--;)
{
int op,x;
io.read(op),io.read(x);
int y=abs(x);
sum+=x;
int rk=T.Find(y);
if(x>0)
{
if(rk<=cnt) pre1+=(y-T.Kth(cnt));
if(rk<=cnt+1) pre2+=(y-T.Kth(cnt+1));
T.insert(y);
if(op==1)
{
cnt++;
pre1+=T.Kth(cnt);
pre2+=T.Kth(cnt+1);
S.insert(y);
}
if(T.Find(*S.begin())==cnt) ans=sum+pre2-*S.begin();
else ans=sum+pre1;
}
else
{
if(rk<=cnt) pre1-=(y-T.Kth(cnt+1));
if(rk<=cnt+1) pre2-=(y-T.Kth(cnt+2));
T.Del(y);
if(op==1)
{
pre1-=T.Kth(cnt);
pre2-=T.Kth(cnt+1);
cnt--;
S.erase(y);
}
if(cnt&&T.Find(*S.begin())==cnt) ans=sum+pre2-*S.begin();
else ans=sum+pre1;
}
io.write(ans);
puts("");
}
return 0;
}