Bernard Madoff is an American former stock broker, investment adviser, non-executive chairman of theNASDAQ stock market, and the admitted operator of what has been described as the largest Ponzi schemein history.Two programmers, Jerome O'Hara and George Perez, who helped Bernard Madoff program to produce falserecords have been indicted. They were accused of conspiracy, falsifying records of a broker dealerand falsifying records of an investment adviser.
In every quarter of these years, the two programmers would receive a data sheet, which provided aseries of profit records a1 a2 ... aN in that period. Due to the economic crisis, the awful recordsmight scare investors away. Therefore, the programmers were asked to falsifying these records intob1 b2 ... bN. In order to deceive investors, any record must be no less than all the records beforeit (it means for any 1 ≤ i < j ≤ N, bi ≤ bj).On the other hand, they defined a risk value for their illegal work: risk value = | a1 - b1 | +| a2 - b2 | + ... + | aN - bN | . For example, the original profit records are 300 400 200 100.If they choose 300 400 400 400 as the fake records, the risk value is 0 + 0 + 200 + 300 = 500.But if they choose 250 250 250 250 as the fake records, the risk value is 50 + 150 + 50 + 150 =400. To avoid raising suspicion, they need to minimize the risk value.Now we will give you some copies of the original profit records, you need to find out the minimalpossible risk value.InputThere are multiple test cases (no more than 20).For each test case, the first line contains one integer N (1 ≤ N ≤ 50000), the next line containsN integers a1 a2 ... aN (-109 ≤ ai ≤ 109). The input will end with N = 0.OutputFor each test case, print a single line that contains minimal possible risk value.Sample Input4300 400 200 1000Sample Output400
此題考察可並優先隊列的應用。
用左偏樹實現。
首先,考慮兩個極端的情況:
1): 若a1 <= a2 <= ... <= an,那麼問題的解顯然是bi = ai(i = 1, 2, ..., n);
2): 若a1 > a2 > ... > an,那麼問題的解是bi = x(x為序列a1, a2, ..., an的中位元)。
所以所有序列的解都不會差於中位元。
最終一定可以將整個序列劃分為若干個區間,使得每個區間的都為該區間的中位元。用左偏樹來維護每個區間的中位元,當前面一個區間的中位元大於該區間的中位元時,合并這兩棵左偏樹即可。
PS:ZOJ的測評系統為Linux,所以輸出64為整型要用“%lld”。
Accode:
#include <cstdio>#include <cstdlib>#include <algorithm>#include <string>#define abs(x) ((x) > 0 ? (x) : -(x))const int maxN = 50010;struct LeftList{ int key, dist; LeftList *lc, *rc; LeftList() {} LeftList(int key): key(key), dist(0), lc(NULL), rc(NULL) {}};LeftList *tr[maxN];int L[maxN], R[maxN], sz[maxN], a[maxN];inline int getint(){ int res = 0; char tmp; bool sgn = 1; do tmp = getchar(); while (!isdigit(tmp) && tmp != '-'); if (tmp == '-') { sgn = 0; tmp = getchar(); } do res = (res << 3) + (res << 1) + tmp - '0'; while (isdigit(tmp = getchar())); return sgn ? res : -res;}inline bool cmp(const LeftList *a, const LeftList *b){return a -> key > b -> key;}//預設為小根,這裡重載比較為大根。LeftList *Merge(LeftList *a, LeftList *b){ if (!a) return b; if (!b) return a;//待合并的兩棵子樹中,若其中一棵為空白,//則直接返回另一棵樹。 if (cmp(b, a)) std::swap(a, b);//為了方便操作,讓根值較大(大根樹)//或較小(小根樹)的樹作為合并後的根。 a -> rc = Merge(a -> rc, b); if (!(a -> lc) || a -> rc && a -> rc -> dist > a -> lc -> dist) std::swap(a -> lc, a -> rc);//調整左右子樹的位置使其滿足左偏樹的性質。//(左子樹的距離不小於右子樹的距離即左偏的性質。) if (!(a -> rc)) a -> dist = 0; else a -> dist = a -> rc -> dist + 1;//更新合并後的樹的距離。 return a;}inline void Del(LeftList *&a) //這裡引用符號“&”不能省。{ a = Merge(a -> lc, a -> rc); return;}int main(){ freopen("Financial_Fraud.in", "r", stdin); freopen("Financial_Fraud.out", "w", stdout); int n; while (n = getint()) { int top = 0; for (int i = 0; i < n; ++i) { tr[++top] = new LeftList(a[i] = getint()); sz[top] = 1; L[top] = R[top] = i;//用L,R指標記錄當前左偏樹所代表的區間。 while (top > 1 && tr[top - 1] -> key > tr[top] -> key) { tr[top - 1] = Merge(tr[top - 1], tr[top]); sz[top - 1] += sz[top]; R[top - 1] = R[top]; for (--top; sz[top] > ((R[top] - L[top]) >> 1) + 1; --sz[top]) Del(tr[top]);//使得合并後的左偏樹的根節點(即最大值)//不超過該區間的中位元,即讓左偏樹中//總節點數不超過該區間中總結點數的一半。 } } long long ans = 0; for (int i = 1; i < top + 1; ++i) for (int j = L[i]; j < R[i] + 1; ++j) ans += abs(a[j] - tr[i] -> key); printf("%lld\n", ans); } return 0;}#undef abs