1. 程式人生 > >(HYSBZ)BZOJ 1588 營業額統計

(HYSBZ)BZOJ 1588 營業額統計

/****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;
}