CF1098D Eels
Solution
先考虑不进行操作时,怎么吞噬可以得到最多的危险次数。
有一个结论是:挑最轻的两条鱼合并,可以使答案最大。
我们简单证明一下:设鱼这个集合为 \(S\) ,那么一次操作就是从中取出两个最小的元素 \(a\) 和 \(b\) ,并把 \(a+b\) 插入 \(S\) 。设某次操作 \(a\) 和 \(b\) 操作是不危险的,那么满足 \(2a\leq b\) 。则 \(b\) 不曾被合并过,不然设 \(b=c+d\) ,其中 \(c\leq d\) ,则 \(d\geq b/2>a\) ,那么 \(d\) 应该是被合并的过的,和我们的结论冲突。
然后可以发现,对所有元素排序,若一个元素对答案产生贡献,是满足 \(w_i\leq 2\cdot\sum_{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\n",ans);
}
return 0;
}