题目大意:
定义“可堆序列”为:可以用该序列的所有元素组成一个小根堆(父子节点的值可以相同),其中父节点在原序列中的位置一定在子节点之前。
给定一个长为N的序列,将它划分成最少的“可堆序列”,并输出任意一种划分方案。
N<=1e5,所有测试点的N之和<=2e6。
利用贪心的思路,假如前K-1个节点已经构成了若干个堆,那么加入第K个节点时,应该在满足条件的前提下,让它的父节点尽可能大,从而给较小的数腾出空间。
如果第K个节点不能加入已有的任何一个堆,就以它为根节点新建一个。(特别地,第一个节点肯定要作为根节点。)
维护时可以用std::set或std::map,注意已有两个孩子的节点要及时删除。
另外要注意“所有测试点的N之和<=2e6”这种限制,测试数据的组数可能会达到百万量级,因此执行memset等初始化操作时一定要控制时间复杂度,memset(arr, b, sizeof(arr))这样的写法是会导致TLE的。
(这点看了别人的代码才意识到,之前还以为是STL常数的问题,然后又是reserve,又是emplace_hint,又是二维数组改一维,一边优化一边还纳闷ZOJ既然开了-O2那么STL不应该这么慢啊……果然是我沙茶了)
(顺便庆祝一下ZOJ支持C++11,撒花ヾ(◍°∇°◍)ノ゙)
代码如下:
#include <cctype> #include <climits> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <forward_list> #include <list> #include <map> #include <queue> #include <set> #include <stack> #include <unordered_map> #include <unordered_set> #include <vector> #include <algorithm> #include <functional> #include <iostream> #include <iterator> #include <numeric> #include <regex> #include <stdexcept> #include <string> #include <tuple> #include <utility> #define FILL(arr, ch) memset(arr, ch, sizeof(arr)) using namespace std; using LL = long long; template <class Tp> using MinHeap = priority_queue<Tp, vector<Tp>, greater<Tp>>; template <class Tp> using MaxHeap = priority_queue<Tp>; template <class RAIter> inline void sortGt(RAIter first, RAIter last) { sort(first, last, greater<decltype(*first)>() ); } const double eps = 1e-8; const int inf = 0x3f3f3f3f; const long long inf64 = 0x3f3f3f3f3f3f3f3fLL; inline bool isEq(double x, double y) { return fabs(x - y) <= eps; } inline bool isLt(double x, double y) { return y - x > eps; } inline bool isGt(double x, double y) { return x - y > eps; } inline int read() { char ch = getchar(); while (!isdigit(ch)) ch = getchar(); int x = ch - '0'; while (ch = getchar(), isdigit(ch)) { x *= 10; x += ch - '0'; } return x; } const int maxN = 1e5 + 10; int A[maxN]; int N; void input() { N = read(); for (int* it = A + 1; it <= A + N; ++it) *it = read(); } const int noChild = -1; vector<int> nodes; int ch0[maxN], ch1[maxN]; bool vis[maxN]; void init() { memset(ch0, -1, sizeof(int) * (N + 1)); memset(ch1, -1, sizeof(int) * (N + 1)); memset(vis, 0, sizeof(bool) * (N + 1)); } inline bool addChild(int f, int c) { if (ch0[f] == noChild) { ch0[f] = c; return false; } else { ch1[f] = c; return true; } } void printHeap(int headId) { nodes.clear(); nodes.push_back(headId); for (size_t i = 0; i < nodes.size(); ++i) { int cur = nodes[i]; vis[cur] = true; if (ch0[cur] != noChild) { nodes.push_back(ch0[cur]); if (ch1[cur] != noChild) nodes.push_back(ch1[cur]); } } printf("%zu", nodes.size()); sort(nodes.begin(), nodes.end()); for (int v: nodes) printf(" %d", v); putchar(' '); } void solve() { multimap<int, int, greater<int>> nodeSet; int heapN = 0; init(); for (int i = 1; i <= N; i++) { auto iter = nodeSet.lower_bound(A[i]); if (iter == nodeSet.end()) { heapN += 1; nodeSet.emplace_hint(iter, A[i], i); } else if (addChild(iter->second, i)) { nodeSet.erase(iter); nodeSet.emplace(A[i], i); } else nodeSet.emplace_hint(iter, A[i], i); } printf("%d ", heapN); for (int i = 1; i <= N; i++) if (!vis[i]) printHeap(i); } //#include <boost/progress.hpp> int main() { //boost::progress_timer timer(cerr); int T; scanf("%d", &T); nodes.reserve(maxN); while (T--) { input(); solve(); } return 0; }