http://acm.hdu.edu.cn/showproblem.php?pid=5884
原来求一次哈夫曼可以有O(n)的做法。
具体是,用两个队列,一个保存原数组,一个保存k个节点合并的数值,然后每次选k个的时候,用two point在两个队列中选k个出来即可。
然后又把新的节点放去第二个队列那里去。
所以,首先需要排序(这样就和求 一次huffman编码的时间复杂度一样了nlogn)
然后这样明显第二个队列是单调递增的。
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; const int maxn = 100000 + 20; int a[maxn]; int n, T; LL que[2][maxn], head[2], tail[2]; LL en = 1e14; bool check(int val) { head[0] = tail[0] = head[1] = tail[1] = 0; int res = n % (val - 1); if (res == 0) { res = val - 1; } LL ans = 0; if (res == 1) { for (int i = 1; i <= n; ++i) que[0][tail[0]++] = a[i]; } else { int sum = 0; for (int i = 1; i <= res; ++i) sum += a[i]; for (int i = res + 1; i <= n; ++i) que[0][tail[0]++] = a[i]; ans = sum; que[1][tail[1]++] = sum; } while (true) { if (ans > T) return false; LL sum = 0; int want = val; while (want--) { LL mi1 = 1e14, mi2 = 1e14; if (head[0] < tail[0]) mi1 = que[0][head[0]]; if (head[1] < tail[1]) mi2 = que[1][head[1]]; if (mi1 < mi2) sum += mi1, head[0]++; else sum += mi2, head[1]++; if (mi1 == en && mi2 == en) return true; // 不可以取了 } que[1][tail[1]++] = sum; ans += sum; } return true; } void work() { scanf("%d%d", &n, &T); for (int i = 1; i <= n; ++i) scanf("%d", a + i); sort(a + 1, a + 1 + n); int be = 2, en = n; // cout << check(2) << endl; while (be <= en) { int mid = (be + en) >> 1; if (check(mid)) en = mid - 1; else be = mid + 1; } printf("%d ", be); } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif int t; scanf("%d", &t); while (t--) work(); return 0; }