这个题正确做法是二分K然后去验证
每次验证都是K叉哈弗曼树,也就是排序完了在每次合并最小的K个数
但是要注意一点
开始有N个数字,归并完应该是1个,也就是合并了N-1个
每次归并会把K个数字变成1个,也就是每次减少了K-1个
那么如果N-1不是K-1的倍数,需要优先处理前(N-1)%(K-1)+1个最小的数
以上是正确做法,下面再说玄学事情
1.如果用优先队列会慢很多(很快的都是双队列)
2.存起来输出入的数后必须sort一遍
3.因人而异,有的人写的(如果是优先队列版本)需要加输入优化才能起码不返回TLE
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
using namespace std;
typedef long long int ll;
const int N=100000+5;
int t,n,T,x;
int arr[N];
priority_queue<ll,vector<ll>,greater<ll> > qu;
bool solve(int k) {
while(!qu.empty()) qu.pop();
if(k==1) return false ;
if(k>n) return true ;
ll ans=0,sum=0,ls=0;
if((n-1)%(k-1)!=0) {//如果不能每次合并都是正好K个,先处理前面不足K个的那些数
ls=(n-1)%(k-1)+1;
for(int i=1;i<=ls;i++)
sum+=arr[i];
ans+=sum;
qu.push(sum);
}
for(int i=ls+1; i<=n; i++)
qu.push(arr[i]);//把没处理的放进优先队列
while(qu.size()>=k) {//just K叉哈弗曼树
sum=0;
for(int i=1; i<=k; i++) {
sum+=qu.top();
qu.pop();
}
qu.push(sum);
ans+=sum;
if(ans>T) return false ;
}
return true;
}
int main()
{
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&T);
for(int i=1; i<=n; i++)
scanf("%d",&arr[i]);
sort(arr+1,arr+1+n);//先排序,不然TLE
int l=1,r=100000,mid;
while(l<r){
mid=(l+r)/2;
if(!solve(mid)) l=mid+1;
else r=mid;
}
printf("%d\n",l);
}
return 0;
}