题目大意:
有
n
(
n
≤
1
e
5
)
n(n\le 1e5)
n(n≤1e5) 堆硬币依次排列,每一堆有
a
i
(
a
i
≤
1
e
9
)
a_i(a_i\le 1e9)
ai(ai≤1e9) 个。每堆硬币全是真币或全是假币,真币每个重
5
5
5 克,假币每个重
4
4
4 克。你有一台电子天平,可以从每堆硬币中挑出若干个进行一次称量(也可以一个都不选)。现在你想要知道,若要确定前1,2,……,n堆硬币的真假,至少要称量几次
题目分析:
我们考虑这个问题的简化版,给你
10
10
10 堆金币,有一堆是假的如何一次称出哪一堆是假的
我们可以给每堆进行编号,然后每堆取编号个金币,最后将取出的金币称一下质量,设取出了
n
n
n 枚金币,称的质量为
m
m
m ,那么容易知道编号为
5
∗
n
−
m
5*n-m
5∗n−m 的那一堆就是假币
我们知道这个道理后不难想出我们取
1
,
2
,
4
,
8
,
.
.
.
1,2,4,8,...
1,2,4,8,... 这样的二的幂次堆就可以唯一确定哪些是真币哪些是假币了,那么此时问题就转化成了前
i
i
i 堆可以取出像
1
,
2
,
4
,
8
,
.
.
.
1,2,4,8,...
1,2,4,8,... 这样的多少堆金币
我们考虑对每一个数求出
≤
\le
≤ 这个数的最大的二的幂次,将指数存在桶里
求解答案时我们依次查看编号为
1
1
1~
30
30
30 的桶中的数字
当前的桶中的数字
>
>
> 桶的编号与组数(答案)的乘积,此时表明我们分的组不够多,应该增加 多出来的数/当前桶的编号(要上取整)那么多的组,上取整是因为需要多增加一组存下取整剩下的不够一组的那些数
具体细节见代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
int read()
{
int res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 1e5+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int n,mp[maxn];
int main()
{
n = read();
int cnt = 1,ans = 1;
for(int i = 1;i <= n;i++)
{
int base = 0,tmp = read();
while(tmp)
{
++base;
tmp /= 2;
}
mp[base]++;
int ans = 0,num = 0;
for(int j = 1;j <= 30;j++)
{
num += mp[j];
if(num > j*ans)
{
tmp = num-j*ans;
ans += tmp/j;
if(tmp%j) ans++; //上取整
}
}
printf("%d\n",ans);
}
return 0;
}