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;
}
posted @ 2020-10-14 21:57  jasony_sam  阅读(98)  评论(0编辑  收藏  举报