二分,验证。
二分$k$,然后进行验证。有一个地方需要注意一下:如果$n$个数,每次合并$k$个,最后一次不能合$k$个,那么一开始需要补$0$之后再合并才是最优的。合并的时候用优先队列合并时间复杂度过高,可以用两个队列模拟一下,优化掉一个$log$。
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<iostream> using namespace std; typedef long long LL; const double pi=acos(-1.0),eps=1e-6; void File() { freopen("D:\in.txt","r",stdin); freopen("D:\out.txt","w",stdout); } template <class T> inline void read(T &x) { char c=getchar(); x=0; while(!isdigit(c)) c=getchar(); while(isdigit(c)) {x=x*10+c-'0'; c=getchar();} } const int maxn=100010; int n,T,k,a[maxn]; bool check(int x) { queue<LL>Q[2]; for(int i=1;i<=n;i++) Q[0].push(a[i]); int sz=n; LL sum=0,m0=a[n],m1=0; if(n%(k-1)==1){} else { int num; if(n%(x-1)==0) num=x-1; else num=n%(x-1); LL c=0; for(int i=1;i<=num;i++) { c=c+Q[0].front(); Q[0].pop(); } sum=sum+c; if(sum>k) return 0; Q[1].push(c); m1=c; sz=sz-num+1; } if(sz==1) { if(sum>k) return 0; return 1; } while(1) { LL c=0; for(int i=1;i<=min(sz,x);i++) { if((!Q[0].empty())&&(!Q[1].empty())) { if(Q[0].front()<Q[1].front()) { c=c+Q[0].front(); Q[0].pop(); } else { c=c+Q[1].front(); Q[1].pop(); } } else if(!Q[0].empty()) { c=c+Q[0].front(); Q[0].pop(); } else { c=c+Q[1].front(); Q[1].pop(); } } sum=sum+c; if(sum>k) return 0; if(Q[0].empty()) { Q[0].push(c); m0=c; } else if(Q[1].empty()) { Q[1].push(c); m1=c; } else { if(c>=m0) { Q[0].push(c); m0=c; } else if(c>=m1) { Q[1].push(c); m1=c; } } if(sz<=x) break; sz=sz-x+1; } if(sum>k) return 0; return 1; } int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n); int L=2,R=n,ans; while(L<=R) { int m=(L+R)/2; if(check(m)) ans=m,R=m-1; else L=m+1; } printf("%d ",ans); } return 0; }