[bzoj1588][HNOI2002]营业额统计

来源:互联网 发布:成都软件定制开发 编辑:程序博客网 时间:2024/05/10 13:34

Description

给出n个数,求每个数和它前面每个数的差值绝对值的最小值之和。
n<=32767

Solution

很显然求前驱后驱。
可以离散化后用权值线段树二分找。
也可以直接用splay找。
或者还可以离线排完序用双向链表找。
你喜欢就好喽。。。

Orz bzoj上rank1 0ms踩所有人

Code

#include<cstdio>#include<cstring>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define N 50005using namespace std;const int inf=0x7fffffff;int n,x,a,b,ans,tot,root,t[N][2],f[N],key[N];void insert(int &v,int y,int fa) {    if (!v) {v=++tot;key[v]=y;f[v]=fa;return;}    if (key[v]>y) insert(t[v][0],y,v);    else insert(t[v][1],y,v);}int son(int x) {return t[f[x]][1]==x;}void rotate(int x) {    int y=f[x],z=son(x);f[x]=f[y];    if (f[x]) t[f[x]][son(y)]=x;    if (t[x][1-z]) f[t[x][1-z]]=y;    f[y]=x;t[y][z]=t[x][1-z];t[x][1-z]=y;}void splay(int x,int y) {    while (f[x]!=y) {        if (f[f[x]]!=y)            if (son(x)==son(f[x])) rotate(f[x]);            else rotate(x);        rotate(x);    }    if (!y) root=x;}int pre(int x) {    x=t[x][0];    while (x) {        if (t[x][1]) x=t[x][1];        else break;    }    return x;}int suf(int x) {    x=t[x][1];    while (x) {        if (t[x][0]) x=t[x][0];        else break;    }    return x;}int main() {    scanf("%d",&n);    scanf("%d",&x);    insert(root,x,0);ans=x;    fo(i,2,n) {        scanf("%d",&x);        insert(root,x,0);splay(tot,0);        int l=pre(root);if (l) a=x-key[l];else a=inf;        int r=suf(root);if (r) b=key[r]-x;else b=inf;        ans+=min(a,b);    }    printf("%d",ans);}
0 0