题目大意:
有 n(n≤1e5)n(n\le 1e5)n(n≤1e5) 堆硬币依次排列,每一堆有 ai(ai≤1e9)a_i(a_i\le 1e9)ai(ai≤1e9) 个。每堆硬币全是真币或全是假币,真币每个重 555 克,假币每个重 444 克。你有一台电子天平,可以从每堆硬币中挑出若干个进行一次称量(也可以一个都不选)。现在你想要知道,若要确定前1,2,……,n堆硬币的真假,至少要称量几次
题目分析:
我们考虑这个问题的简化版,给你 101010 堆金币,有一堆是假的如何一次称出哪一堆是假的
我们可以给每堆进行编号,然后每堆取编号个金币,最后将取出的金币称一下质量,设取出了 nnn 枚金币,称的质量为 mmm ,那么容易知道编号为 5∗n−m5*n-m5∗n−m 的那一堆就是假币
我们知道这个道理后不难想出我们取 1,2,4,8,...1,2,4,8,...1,2,4,8,... 这样的二的幂次堆就可以唯一确定哪些是真币哪些是假币了,那么此时问题就转化成了前 iii 堆可以取出像 1,2,4,8,...1,2,4,8,...1,2,4,8,... 这样的多少堆金币
我们考虑对每一个数求出 ≤\le≤ 这个数的最大的二的幂次,将指数存在桶里
求解答案时我们依次查看编号为 111~303030 的桶中的数字
当前的桶中的数字 >>> 桶的编号与组数(答案)的乘积,此时表明我们分的组不够多,应该增加 多出来的数/当前桶的编号(要上取整)那么多的组,上取整是因为需要多增加一组存下取整剩下的不够一组的那些数
具体细节见代码:
#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;
}

&spm=1001.2101.3001.5002&articleId=112790590&d=1&t=3&u=9c8d91df3b044e699f00abc4bbbb731e)
976

被折叠的 条评论
为什么被折叠?



