题意:
圣诞老人要给k个小学生分发一些橘子,现在圣诞老人有n个橘子,第i个橘子有ai片。圣诞老人讨厌偏心,所以他想要在场的k个小学生都能拿到一定数量的橘子,不过他的橘子现在没有k个这么多。但是圣诞老人可以将橘子掰开,具体做法是,可以将一个有ai片的橘子掰成尽可能片数相等的两块,如果ai是偶数,那么掰成ai / 2的两块,否则最有一块比另一块多一片。被掰开后的橘子还可以继续这个操作,只要剩余片数大于1,就可以掰开
现在,圣诞老人想要在满足所有小学生都能拿到一定数量橘子的情况下拿到橘子片数最少的小学生拿到的橘子片数最多,输出这个值。 n <= 1E6,k <= 2E9,ai <= 1E7
solution:
考场想了一个O(nlog^2)的做法,,二分答案,每个橘子尽可能地拆开。然而这样好像复杂度很极限啊。。
正解是是线性的。。首先,如果所有橘子的片数加起来不足k,肯定是找不到一个合法方案的。然后,答案显然不超过1E7,从这个值开始往下枚举答案
对于一块有x片的橘子,如果存在含有y片的橘子,y > x,那么在掰开含y片的那个橘子前,显然是可以先不考虑这个橘子的。
假设当前枚举到的答案为Ans,如果一个橘子,掰开后有任意一块的片数小于Ans,那么这个橘子还不如不掰开,反正对答案没有影响。
基于以上两点,可以先将所有橘子按照片数扔进一个桶里面,维护两个指针,一个从大到小枚举Ans,另一个从大到小枚举掰开有贡献的橘子,什么时候能满足所有小学生的需求,什么时候就可以输出了O(max{ai})
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1E7 + 7;
typedef long long LL;
LL k,sum,c[maxn];
int n,A,B;
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
cin >> n >> k;
while (n--)
{
int x; scanf("%d",&x);
++c[x]; sum += 1LL*x;
}
if (sum < k) {cout << -1; return 0;}
int Ans,cur; Ans = cur = 10000000; sum = 0;
for (; ; --Ans)
{
sum += c[Ans];
for (;;)
{
A = cur / 2; B = cur - A;
if (A < Ans || B < Ans) break;
sum += 1LL*c[cur];
c[A] += c[cur]; c[B] += c[cur];
c[cur] = 0; --cur;
}
if (sum >= k) {cout << Ans; return 0;}
}
return 0;
}