题面
题意
在鱼缸中有一些鱼,这些鱼之间会相互吞噬直到只剩下一条鱼,若一条鱼的质量为a,另一条鱼的质量为b,则吞噬之后会剩下一条质量为a+b的鱼,若
a
<
=
b
a<=b
a<=b且
b
<
=
2
∗
a
b<=2*a
b<=2∗a,则这次吞噬是危险的。
现在有一个空鱼缸,进行多次操作,每次操作会加入一条鱼或去掉某一条鱼,问这次操作后,鱼缸中的鱼相互吞噬直到只剩下一条鱼,至多有几次是危险的。
做法
首先考虑怎样吞噬才会使危险次数最多,可以发现如果每次挑选最轻的两条鱼互吞,答案最大,进一步可以发现,对所有鱼按质量进行排序后,若一条鱼与其他鱼合并会对答案造成贡献,当且仅当
w
i
<
=
2
∗
∑
j
<
i
w
j
w_i<=2*\sum_{j<i}{w_j}
wi<=2∗∑j<iwj
现在考虑维护这个东西。
根据鱼的质量将其分为30个区间:
[
1
,
2
)
,
[
2
,
4
)
,
[
4
,
8
)
.
.
.
.
.
.
[
2
29
,
2
30
)
[1,2),[2,4),[4,8)......[2^{29},2^{30})
[1,2),[2,4),[4,8)......[229,230)
然后可以发现,如果一个区间中有多个元素,则只有最小的元素可能不是危险的,因此我们只要维护每个区间的和,每次询问
O
(
30
)
O(30)
O(30)暴力计算即可
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll Q,ans,sum[40];
char str[2];
multiset<ll>num[40];
int main()
{
ll i,j,t,qz;
cin>>Q;
while(Q--)
{
scanf("%s%lld",str,&t);
for(i=1;(1 << i)<=t;i++);i--;
if(str[0]=='+')
{
sum[i]+=t;
num[i].insert(t);
}
else
{
sum[i]-=t;
num[i].erase(num[i].find(t));
}
qz=ans=0;
for(i=0;i<30;i++)
{
if(!num[i].size()) continue;
ans+=(num[i].size()-((*num[i].begin())>2*qz));
qz+=sum[i];
}
printf("%lld\n",ans);
}
}