【最优K叉树】hdu 5884 Sort

http://acm.hdu.edu.cn/showproblem.php?pid=5884

参考:https://www.cnblogs.com/jhz033/p/5879452.html

【题意】

n个有序序列的归并排序.每次可以选择不超过k个序列进行合并,合并代价为这些序列的长度和.总的合并代价不能超过T, 问k最小是多少。

【思路】

k越大,代价越小,二分k,check k

对于每个k,问题转化为n个结点的最优k叉树,用堆做时间复杂度为O(nlogn),再加上二分总复杂度是O(nlogn^2)

然后T了。。。听说加输入挂可以卡过去

正解是这样的:

原数组排序,预处理时间复杂度O(nlogn)

然后求最优k叉树,用一个队列维护合并后的值,而不需要加入原来的堆里重排序(利用新加入的内结点本身的单调性),每次时间复杂度为O(n)

O(nlogn)+O(logn)*O(n),所以最后时间复杂度是O(nlogn)

n个结点不一定能构成最优k叉树,需要补 k-1-((n-1)%(k-1))个权为0的虚结点。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<string>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<cmath>
 8 using namespace std;
 9 typedef long long ll;
10 int n;
11 ll t;
12 const int maxn=1e5+3;
13 ll a[maxn];
14 bool judge(int mid){
15     int cnt=(n-1)/(mid-1);
16     queue<ll> Q;
17     if((n-1)%(mid-1)!=0){
18         cnt++;
19         for(int i=0;i<(mid-1-(n-1)%(mid-1));i++){
20             Q.push(0);
21         }    
22     }
23     ll ans=0;
24     int l=0;
25     while(cnt--){
26         ll tmp=0;
27         for(int i=0;i<mid;i++){
28             if(!Q.empty()&&(l>=n||Q.front()<=a[l])){
29                 tmp+=Q.front();
30                 Q.pop();
31             }else{
32                 tmp+=a[l++];
33             }
34         }
35         ans+=tmp;
36         Q.push(tmp);
37     }
38     if(ans<=t) return true;
39     return false;
40 }
41 int main(){
42     int T;
43     scanf("%d",&T); 
44     while(T--){
45         scanf("%d%lld",&n,&t);
46         for(int i=0;i<n;i++){
47             scanf("%lld",&a[i]);
48         }        
49         sort(a,a+n);
50         int l=2,r=n;
51         while(l<=r){
52             int mid=(l+r)>>1;
53             if(judge(mid)){
54                 r=mid-1;
55             }else{
56                 l=mid+1;
57             }
58         }
59         printf("%d
",l);
60     }
61     return 0;
62 }
View Code
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
int n;
ll t;
const int maxn=1e5+3;
ll a[maxn];
bool judge(int mid){
    queue<ll> Q1,Q2;
    for(int i=0;i<n;i++) Q1.push(a[i]);
    if((n-1)%(mid-1)!=0){
        for(int i=0;i<(mid-1-(n-1)%(mid-1));i++){
            Q2.push(0);
        }    
    }
    ll ans=0;
    while(Q1.size()+Q2.size()>=mid){
        ll tmp=0;
        for(int i=0;i<mid;i++){
            if(Q1.empty()&&!Q2.empty()){
                tmp+=Q2.front();
                Q2.pop();
            }else if(Q2.empty()&&!Q1.empty()){
                tmp+=Q1.front();
                Q1.pop();
            }else if(!Q1.empty()&&!Q2.empty()){
                if(Q1.front()<=Q2.front()){
                    tmp+=Q1.front();
                    Q1.pop();
                }else{
                    tmp+=Q2.front();
                    Q2.pop();
                }
            }
        }
    //    cout<<tmp<<endl;
        ans+=tmp;
        Q2.push(tmp);
    }
    if(ans<=t) return true;
    return false;
}
int main(){
    int T;
    scanf("%d",&T); 
    while(T--){
        scanf("%d%lld",&n,&t);
        for(int i=0;i<n;i++){
            scanf("%lld",&a[i]);
        }        
        sort(a,a+n);
        int l=2,r=n;
        while(l<=r){
            int mid=(l+r)>>1;
            if(judge(mid)){
                r=mid-1;
            }else{
                l=mid+1;
            }
        }
        printf("%d
",l);
    }
    return 0;
}