(HYSBZ)BZOJ 1588 營業額統計
阿新 • • 發佈:2019-01-29
/****Spaly的基本操作和應用**** 求前驅與root的差和後驅與root的差,取最大,累加即可. Rotate寫的很精簡,是學習別人的; = =!他的原始碼有錯誤,不過卻又不影響結果...... 以後看別人的東西也要小心~~ *****************************/ #include <map> #include <set> #include <list> #include <queue> #include <stack> #include <cmath> #include <ctime> #include <vector> #include <bitset> #include <cstdio> #include <string> #include <numeric> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <functional> using namespace std; typedef long long ll; typedef unsigned long long ull; #define eps 1e-8 #define inf 0x7fffffff #define debug puts("BUG") #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define read freopen("in.txt","r",stdin) #define write freopen("out.txt","w",stdout) #define Mem(a,x) memset(a,x,sizeof(a)) #define maxn 100005 int pre[maxn],key[maxn],ch[maxn][2],root,tot1; //分別表示父結點,鍵值,左右孩子(0為左孩子,1為右孩子),根結點,結點數量 int n; int ans; void init() { root=tot1=0; ans=0; Mem(key,0); } void NewNode(int &r,int father,int k) { r=++tot1; //記錄新節點的位置 pre[r]=father;//更新新節點的父親 key[r]=k; ch[r][0]=ch[r][1]=0;//左孩子和右孩子為空 } void Rotate(int x,int k)//k為左旋和右旋的標誌 { int y=pre[x];/**記錄x的父親**/ ch[y][!k]=ch[x][k]; pre[ch[x][k]]=y; /**如果k=0, 表示從左到右旋轉;如果k=1,表示從右到左旋轉 *k=0, 表示x的位置由它的右孩子代替; *k=1, 表示x的位置由它的左孩子代替. *x的位置, 即為 !k **/ if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; /** 更新x的父親 和 x的新父親的孩子節點(如果新的父親存在) * 如上 **/ ch[x][k]=y; pre[y]=x; /**更新y的父親(即為x) 和 x的孩子節點 *如上 **/ } void Splay(int r,int goal)//Splay調整,將r的值調整到目標位置goal { while(pre[r]!=goal) { if(pre[pre[r]]==goal)//如果r的父親就是根節點,goal的值為0,直接進行一次旋轉即可 { Rotate(r,ch[pre[r]][0]==r); } else//否則 { int y= pre[r]; int k= ch[pre[y]][0]==y;//記錄y與y的父親的方向 if(ch[y][k]==r)//判斷x與y和y的父親是否在同一方向 { //是 , 進行一字型旋轉 Rotate(r,!k);//先左(右)旋 Rotate(r, k);//後右(左)旋 } else { //否 , 進行之字形旋轉 Rotate(y,k);//先對y進行左(右)旋 Rotate(r,k);//後對x進行右(左)旋 } } } /**更新根節點**/ if(goal==0) root=r; } int Insert(int k) //將鍵值k插入到Splay中(Splay不為空) { int r=root; //從根節點開始 while(ch[r][key[r]<k]) { //一直向下,找到葉子節點,根據二叉排序樹的性質,查詢時只需要比較key[r]與k的大小 if(key[r]==k) { //同一個值不需要再次插入,只需要進行Splay操作 Splay(r,0); return 0; //表示插入失敗 } r=ch[r][key[r]<k]; //未找到葉子節點,就需繼續向下找 } if(key[r]==k) { //同一個值不需要再次插入,只需要進行Splay操作 Splay(r,0); return 0; //表示插入失敗 } NewNode(ch[r][k>key[r]],r,k); //新建節點 Splay(ch[r][k>key[r]],0); //Slay操作,插入的節點調整至根節點 return 1; //表示插入成功 } int Get_pre(int x)//尋找前驅,即尋找左子樹的最右點 { int temp=ch[x][0]; if(temp==0) return inf; while(ch[temp][1]) temp=ch[temp][1]; return key[x]-key[temp]; } int Get_next(int x)//尋找後繼,即尋找右子樹的最左點 { int temp=ch[x][1]; if(temp==0) return inf; while(ch[temp][0]) temp=ch[temp][0]; return key[temp]-key[x]; } /********************DEBUG***************************/ void Puts() { printf("root: %d\n",root); for(int i=1;i<=tot1;i++) { printf("node: %d ,l is %d ,r is %d ,key : %d\n",i,ch[i][0],ch[i][1],key[i]); } } /********************DEBUG END***********************/ int main() { while(~scanf("%d",&n)) { init(); for(int i=1; i<=n; i++) { int x; scanf("%d",&x); if(i==1) { ans+=x; NewNode(root,0,x);//建立Splay的第一個節點 continue; } if(Insert(x)==0)//插入,已經存在的值不需要插入 {continue;} int a=Get_pre(root);//與前驅的差,可能不存在 int b=Get_next(root);//與後驅的差,也可能不存在 ans+=min(a,b); } printf("%d\n",ans); } return 0; }