【uoj#280】[UTR #2]題目難度提升 對頂堆+STL-set
阿新 • • 發佈:2018-03-31
元素 奇數 CP href 數列 target 維護 fine 時間
題目描述
給出 $n$ 個數 $a_1,a_2,...,a_n$ ,將其排為序列 $\{p_i\}$ ,滿足 $\{前\ i\ 個數的中位數\}$ 單調不降。求字典序最大的 $\{p_i\}$ 。
其中,對於一個長度為 $m$ 的數列,若 $m$ 為奇數,則中位數為從小到大第 $\lceil\frac m2\rceil$ 大的數;若 $m$ 為偶數,則中位數為從小到大第 $\frac m2$ 大和第 $\frac m2+1$ 大的數的平均值。
題解
對頂堆+STL-set
顯然如果已經知道了這個數列的一部分,剩下的一定是每次加入大於等於中位數的數。
那麽如何確定這一“部分呢”?將 $a$ 從小到大排序,然後:
- 如果 $a_{\lceil\frac n2\rceil}=a_{\lceil\frac n2+1\rceil}$ ,則可以讓任何時刻中位數都等於 $a_{\lceil\frac n2\rceil}$ ,找到最大的 $k$ 使得 $a_k+1=a_{\lceil\frac n2\rceil}$ ,按照 $k,k+1,k-1,k+2,k-2,...$ 的順序選擇完整個數列即可得到最優解,顯然任何時刻中位數都相等。沒有考慮到這種情況可以得到60分。
- 否則如果存在 $k<\lceil\frac n2\rceil$ 且 $a_k=a_{k+1}$ ,則按照 $k,k+1,k-1,k+2,k-2,...$ 的順序選擇,直到前面沒有數可以取,這個過程中位數都相等。沒有考慮到這種情況只能得到所有數互不相同的40分。
- 否則選擇第一個數。
然後使用multiset保證每次刪除後最小的數大於等於中位數,使用對頂堆維護中位數即可。
對頂堆:使用大根堆維護較小數,使用小根堆維護大數,保證兩個堆的大小差不超過1。顯然中位數可以直接從兩個堆的堆頂元素得到。
時間復雜度 $O(n\log n)$ 。
#include <set> #include <queue> #include <cstdio> #include <cstring> #include <algorithm> #define N 100010 using namespace std; multiset<int> s; priority_queue<int> A , B; int a[N] , v[N]; inline void push(int x) { if(A.empty() || x <= A.top()) A.push(x); else B.push(-x); if(A.size() < B.size()) A.push(-B.top()) , B.pop(); if(A.size() - B.size() > 1) B.push(-A.top()) , A.pop(); } int main() { int T; scanf("%d" , &T); while(T -- ) { memset(v , 0 , sizeof(v)); while(!A.empty()) A.pop(); while(!B.empty()) B.pop(); int n , i , p , q , mid; scanf("%d" , &n); for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]); sort(a + 1 , a + n + 1) , mid = (n + 1) >> 1; if(a[mid] == a[mid + 1]) { while(mid < n && a[mid] == a[mid + 1]) mid ++ ; printf("%d" , a[mid]) , p = mid - 1 , q = n; while(p || q > mid) { if(p) printf(" %d" , a[p -- ]); if(q > mid) printf(" %d" , a[q -- ]); } continue; } while(mid > 1 && a[mid] != a[mid - 1]) mid -- ; printf("%d" , a[mid]) , v[mid] = 1 , p = mid - 1 , q = n; while(p && q > mid) printf(" %d" , a[p]) , v[p -- ] = 1 , printf(" %d" , a[q]) , v[q -- ] = 1; for(i = 1 ; i <= n ; i ++ ) { if(v[i]) push(a[i]); else s.insert(a[i]); } while(!s.empty()) { p = *s.begin(); if(A.size() == B.size()) { if(p >= -B.top()) q = *--s.end(); else q = *s.begin(); } else { if(!B.empty() && p * 2 >= A.top() - B.top()) q = *--s.end(); else q = *--s.upper_bound(p * 2 - A.top()); } printf(" %d" , q) , s.erase(s.find(q)) , push(q); } if(T) puts(""); } return 0; }
【uoj#280】[UTR #2]題目難度提升 對頂堆+STL-set