hdu_5884_Sort(二分+单调队列)

时间:2022-09-01 00:05:24

题目链接:hdu_5884_Sort

题意:

有n个数,每个数有个值,现在你可以选择每次K个数合并,合并的消耗为这K个数的权值和,问在合并为只有1个数的时候,总消耗不超过T的情况下,最小的K是多少

题解:

首先要选满足条件的最小K,肯定会想到二分。

然后是如何来写这个check函数的问题

我们要贪心做到使消耗最小,首先我们将所有的数排序

然后对于每次的check的mid都取最小的mid个数来合并,然后把新产生的数扔进优先队列,直到最后只剩一个数。

不过这样的做法是n*(logn)2 ,常数写的小,带优化能卡过去,不过我反正卡不过去,然后就需要一个数据结构优化一个log

那就是用单调队列,可以先看看这篇文章  合并果子

然而这里有个小细节,不注意是过不去的:

对于n个数,一共要合并n-1个数,才能最后剩一个数,然后对于每次的mid,每次会合并mid-1个数,如果(n-1)%(mid-1)!=0,那么我们得先取(n-1)%(mid-1)+1个数来合并,这样后面合并的时候才能刚合适,如果不取,读者可以自己模拟一下,会出错。

 #include<bits/stdc++.h>
#define F(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
typedef long long ll; const int N=1e5+;
int t,n,T,a[N];
ll inf=1e18;
queue<ll>Q1,Q2; bool check(int mid)
{
ll ans=;
while(!Q1.empty())Q1.pop();
while(!Q2.empty())Q2.pop();
F(i,,n)Q1.push(a[i]);
int num=(n-)%(mid-);
if(num)
{
ll tp=;
F(i,,num+)tp+=Q1.front(),Q1.pop();
ans+=tp;
Q2.push(tp);
}
while()
{
ll tp=;
F(i,,mid)
{
ll x=inf,y=inf;
if(Q1.empty()&&Q2.empty())break;
if(!Q1.empty())x=Q1.front();
if(!Q2.empty())y=Q2.front();
if(x<y)tp+=x,Q1.pop();
else tp+=y,Q2.pop();
}
ans+=tp;
if(ans>T)return ;
if(Q1.empty()&&Q2.empty())break;
Q2.push(tp);
}
return ans<=T;
} int main(){
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&T);
F(i,,n)scanf("%d",a+i);
sort(a+,a++n);
int l=,r=n,mid,ans;
while(l<=r)mid=(l+r)>>,check(mid)?r=mid-,ans=mid:l=mid+;
printf("%d\n",ans);
}
return ;
}