1. 程式人生 > 實用技巧 >[Hnoi2002]營業額統計

[Hnoi2002]營業額統計

Tiger最近被公司升任為營業部經理,他上任後接受公司交給的第一項任務便是統計並分析公司成立以來的營業情況。Tiger拿出了公司的賬本,賬本上記錄了公司成立以來每天的營業額。分析營業情況是一項相當複雜的工作。由於節假日,大減價或者是其他情況的時候,營業額會出現一定的波動,當然一定的波動是能夠接受的,但是在某些時候營業額突變得很高或是很低,這就證明公司此時的經營狀況出現了問題。經濟管理學上定義了一種最小波動值來衡量這種情況:該天的最小波動值= min { | 該天以前某一天的營業額-該天的營業額 | }當最小波動值越大時,就說明營業情況越不穩定。而分析整個公司的從成立到現在營業情況是否穩定,只需要把每一天的最小波動值加起來就可以了。你的任務就是編寫一個程式幫助Tiger來計算這一個值。 注:第一天的最小波動值為第一天的營業額。 資料範圍:天數n≤32767,每天的營業額ai≤1,000,000。最後結果T≤2^31

輸入
第一行為正整數 ,表示該公司從成立一直到現在的天數 接下來的n行每行有一個正整數 ,表示第i天公司的營業額

輸出
輸出檔案僅有一個正整數,即Sigma(每天的最小波動值)。結果小於2^31

樣例
輸入
6
5
1
2
5
4
6
輸出
12
//結果說明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100050;
int n,ans,rt,tot;
int v[maxn],son[maxn][2],f[maxn];
int read(){
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
int get_pre(int x)
{
    int node=son[x][0];
    while(son[node][1])
	     node=son[node][1];
    return v[node];
}
int get_suc(int x)
{
    int node=son[x][1];
    while(son[node][0])
	     node=son[node][0];
    return v[node];
}
int t(int x)//看當前點是否為其父親的右兒子 
{
    if(son[f[x]][1]==x)
	   return 1;
    else return 0;
}
void move(int x)
{
    int fa=f[x];//取出x的父親點 
	int s=son[x][t(x)^1];//假設t(x)=1,則此時取出x的左兒子的編號 
	int ret=t(x);//ret代表x是其父親哪個子結點,0為左,1為右 
	//以下重新建立x的左結點與x父親之間的關係 
    son[x][ret^1]=fa;//x的從前的父親變為x左兒子 
    son[fa][ret]=s;//x的左兒子變為fa的右兒子 
    if(s)  //如果s不為空,則s的父親變為fa 
	   f[s]=fa;
    //以下重新建立x與其祖父點的關係 
	f[x]=f[fa];//x的父親變為其從前的祖父 
    
    if(f[x])
	//如果x有父親,則x變為保持從前父親點與之的關係。
	//即從前是左兒仍是左兒,從前是右兒仍是右兒
	   son[f[x]][t(fa)]=x;
    f[fa]=x;
}
void splay(int x)
{
    while(f[x])
	{
        if(f[f[x]])
		{
            if(t(f[x])==t(x))
            //如果是一字型的就旋轉x的父親點 
			     move(f[x]);
            else 
            //否則旋轉x結點本身 
			     move(x);
        }
        move(x);
        //再旋轉x結點本身 
    }
    rt=x;
}
void insert(int x)
{
    v[++tot]=x;
    if(!rt)
	{
    	
	   rt=tot;
	   return;
	}
    int node=rt;
    while(1)
	{
        if(x<=v[node])
         //x小於v結點的值 
		{
            if(!son[node][0])
            //正好v結點左子結點為空 
			  {
			         f[tot]=node;
			         //第tot的結點的父親點為node 
					 son[node][0]=tot;
					 //node結點的左子結點為tot結點 
					 break;
			  }
            node=son[node][0];
            //node的左子結點不為空,於是node走到左子結點 
        }
        else
		{
            if(!son[node][1])
			   {
			         f[tot]=node;
					 son[node][1]=tot;
					 break;
			  }
            node=son[node][1];
        }
    }
    splay(tot);
}
int main(){
    n=read();
    for(int i=1;i<=n;i++)
	{
        int x=read();
		insert(x);
        if(i==1)
		  {
		      ans=x;
			  continue;
		  }
        if(son[rt][0]&&son[rt][1])//如果有前趨和後繼,則取兩者最小值減去當前值 
            ans+=min(v[rt]-get_pre(rt),get_suc(rt)-v[rt]);
        else
		{
            if(son[rt][0])  //如果有左兒子,則用當前值減它的前趨
			   ans+=v[rt]-get_pre(rt);
            else 
			   ans+=get_suc(rt)-v[rt];
        }
    }
    printf("%d\n",ans);
    return 0;
}