BZOJ 1588: [HNOI2002]營業額統計 (Splay入門題目)
Description 營業額統計 Tiger最近被公司升任為營業部經理,他上任後接受公司交給的第一項任務便是統計並分析公司成立以來的營業情況。 Tiger拿出了公司的賬本,賬本上記錄了公司成立以來每天的營業額。分析營業情況是一項相當複雜的工作。由於節假日,大減價或者是其他情況的時候,營業額會出現一定的波動,當然一定的波動是能夠接受的,但是在某些時候營業額突變得很高或是很低,這就證明公司此時的經營狀況出現了問題。經濟管理學上定義了一種最小波動值來衡量這種情況: 該天的最小波動值 當最小波動值越大時,就說明營業情況越不穩定。 而分析整個公司的從成立到現在營業情況是否穩定,只需要把每一天的最小波動值加起來就可以了。你的任務就是編寫一個程式幫助Tiger來計算這一個值。 第一天的最小波動值為第一天的營業額。 輸入輸出要求
Input 第一行為正整數 ,表示該公司從成立一直到現在的天數,接下來的n行每行有一個整數(有可能有負數) ,表示第i 天公司的營業額。 天數n<=32767, 每天的營業額ai <= 1,000,000。 最後結果T<=2^31 Output 輸出檔案僅有一個正整數,即Sigma(每天最小的波動值) 。結果小於2^31 。
Sample Input 6
5
1
2
5
4
6 Sample Output 12
HINT 結果說明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12
該題資料bug已修復.----2016.5.15
題目很簡單,甚至直接用set就能搞過去。但是為了學習下splay,所以做了這個題目
Splay就是一種伸展樹,每次插入一個新數,都會把這個數扭到根的位置,而且保持著左邊比他小,右邊比他大的,二叉搜尋樹的性質。因為普通的二叉搜尋樹,在有序的數輸入下,會退化為一條鏈,所以有了splay我們可以保持樹的相對平衡,使得每次插入,查詢等操作的複雜度都是O(logN)的。 對於這個題目,我們只需要每次把數插進入,尋找左子樹中最右邊的節點,和右子樹種最左邊的即可。注意查詢不到的情況。
程式碼如下:
#include<bits/stdc++.h> using namespace std; const int MAX = 40010*4; const int INF = 0x3f3f3f3f; int ch[MAX][2],f[MAX],key[MAX]; int root,sz; typedef long long ll; inline int get(int x){ return ch[f[x]][1] == x; } inline void Rotate(int x){ int y = f[x],z = f[y]; int kind = get(x); ch[y][kind] = ch[x][!kind]; f[ch[y][kind]] = y; f[y] = x;ch[x][!kind] = y; f[x] = z; if(z) ch[z][ch[z][1] == y] = x; } inline void Splay(int x){ for(int fa;(fa = f[x]);Rotate(x)){ if(f[fa]) Rotate((get(x)==get(fa))? fa:x); } root = x; } inline bool Insert(int x){ if(root == 0){ sz++; ch[sz][0] = ch[sz][1] = f[sz] = 0; root = sz;key[sz] = x; return true; } int now = root,fa = 0; while(true){ if(key[now] == x){ Splay(now); return false; } fa = now; now = ch[now][x > key[now]]; if(now == 0){ sz++; ch[sz][0] = ch[sz][1] = 0; root = sz;key[sz] = x; f[sz] = fa; ch[fa][x > key[fa]] = sz; Splay(sz); return true; } } } int t1,t2; void QueryPre(){ int k = ch[root][0]; if(!k) return; while(ch[k][1]) k = ch[k][1]; t1 = key[k]; } void QueryNex(){ int k = ch[root][1]; if(!k) return; while(ch[k][0]) k = ch[k][0]; t2 = key[k]; } int main(void){ int N,x; ll res = 0; scanf("%d",&N); for(int i=1;i<=N;++i){ scanf("%d",&x); if(!Insert(x)) continue; if(i == 1) res += x;//第一個數,查不到前驅和後繼。 else{ t1 = -INF,t2 = INF; QueryPre(); QueryNex(); //printf("%d %d\n",t1,t2); res += min(x-t1,t2-x); } } printf("%lld\n",res); return 0; }