Solution
先考虑不进行操作时,怎么吞噬可以得到最多的危险次数。
有一个结论是:挑最轻的两条鱼合并,可以使答案最大。
我们简单证明一下:设鱼这个集合为 (S) ,那么一次操作就是从中取出两个最小的元素 (a) 和 (b) ,并把 (a+b) 插入 (S) 。设某次操作 (a) 和 (b) 操作是不危险的,那么满足 (2aleq b) 。则 (b) 不曾被合并过,不然设 (b=c+d) ,其中 (cleq d) ,则 (dgeq b/2>a) ,那么 (d) 应该是被合并的过的,和我们的结论冲突。
然后可以发现,对所有元素排序,若一个元素对答案产生贡献,是满足 (w_ileq 2cdotsum_{j<i}w_j) 的。
也就是只要能维护这个判断就行了——根据 (w_i) 分等级: ([2^0,2^1),[2^1,2^2),[2^2,2^3),cdots,[2^{29},2^{30})) ,然后发现一个等级中如果有多个元素,只有最小的可能不是危险的(这个我不会证qwq),因此维护和即可。
如果有插入和删除操作直接改变区间和和元素就好了。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll q,ans,sum[40];
char ch[2];
multiset<ll> num[40];
int main(){
scanf("%lld",&q);
while(q--){
ll x,i;
scanf("%s%lld",ch,&x);
for(i=1;(1<<i)<=x;i++);i--;
if(ch[0]=='+'){
sum[i]+=x;
num[i].insert(x);
}
else{
sum[i]-=x;
num[i].erase(num[i].find(x));
}
ll siz=0,ans=0;
for(int i=0;i<30;i++){
if(!num[i].size()) continue;
ans+=(num[i].size()-((*num[i].begin())>2*siz));
siz+=sum[i];
}
printf("%lld
",ans);
}
return 0;
}