bzoj 1588 //1588: [HNOI2002]营业额统计

bzoj 1588 //1588: [HNOI2002]营业额统计   //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1588
//在线测评地址https://www.luogu.org/problem/P2234

更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录

Splay入门,详见此文https://blog.csdn.net/mrcrack/article/details/103196131

Accepted 1468 kb 228 ms C++/Edit 3623 B

//1588: [HNOI2002]营业额统计
//在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1588
//在线测评地址https://www.luogu.org/problem/P2234
//有了结果说明,题意还是好理解的。2019-11-17 20:37
//O(n^2)算法肯定超时。
//splay感觉细节挺多的,靠看,很难掌握,靠理解,细节太多,还是开始编码吧,在编码的过程中进行体会,一遍不行,两遍吧。
//也不再固执,用上C++类的特性吧。2019-11-26
#include
using namespace std;
#define maxn 32867//32767+100
#define INF 1e7//因1e6-0=1e6,这是最大差值,故1e7足够用了
int cnt=0;//cnt记录节点插入树中的时间顺序。
struct Splay_Tree{
    int rt;//根节点,记录节点的时间戳。
    int val[maxn],size[maxn],fa[maxn],ch[maxn][2];//一个节点需要具备的信息,值,所含节点数(包括自己),父亲是谁,孩子是谁。ch[][0]左孩子,ch[][1]右孩子,如何确定节点,靠时间戳。
    int max(int a,int b){
        return a>b?a:b;
    }
    int min(int a,int b){
        return a     }
    void insert(int v){//插入节点值
        int pos,posfa,f;//此处错写成int pos,posfa;//posfa记录pos的父亲
        if(++cnt!=1){//插入的节点不是第1个点。
            pos=rt;//按二叉查找树的方式插入
            while(size[pos]){//一定要找到底,到底,size[]为0,找到该节点插入位置;从何处找起,确定一棵树的起始,当然是根。
                if(v<=val[pos])posfa=pos,pos=ch[pos][0],f=0;//此处错写成if(v<=val[pos])posfa=pos,pos=ch[pos][0];//左树
                else posfa=pos,pos=ch[pos][1],f=1;//此处错写成else posfa=pos,pos=ch[pos][1];//右树
            }
            fa[cnt]=posfa,size[cnt]=1,val[cnt]=v,ch[posfa][f]=cnt;//此处错写成fa[pos]=posfa,size[pos]=1,val[pos]=v;//只要找父亲,及确定自己的值即可,没有孩子。
            splay(cnt,0);//平衡树 将v拧到树根。
        }else val[cnt]=v,size[cnt]=1,rt=cnt;//第一个节点插入树中。
    }
    int Query(int v){
        int pos=rt,Pre,Nex;
        while(size[pos]){//此处错写成while(1){
            if(v             else if(v==val[pos]){Pre=Nex=v;break;}
            else if(v>val[pos])Pre=val[pos],pos=ch[pos][1];//此处错写成else if(v>val[pos])pos=ch[pos][1],Pre=val[pos];
        }
        return min(Nex-v,v-Pre);
    }
    void splay(int x,int pos){//写在struct里的好处是,不用注意函数的前后顺序了
        int y,z,f1,f2;
        for(y=fa[x],z=fa[y];y!=pos;y=fa[x],z=fa[y]){//此处错写成for(y=fa[x],z=fa[y];pos;x=fa[y],y=fa[z]){//自x处开始拧
            f1=ch[y][1]==x,f2=ch[z][1]==y;
            if(z==pos)rotate(x,f1);//无爷爷
            else if(f1^f2)rotate(x,f1),rotate(x,f2);//子父,父爷 连线不共线  拧2次x
            else rotate(y,f2),rotate(x,f1);//子父,父爷 连线 共线 先拧y,再拧x
        }
        update(x);//漏了此句
        if(pos==0)rt=x;//任命x为新的根
    }
    void rotate(int x,int f){
        int y=fa[x],z=fa[y],u=ch[x][f^1];
        if(z)ch[z][ch[z][1]==y]=x;//此处错写成fa[x]=z,ch[z][ch[z][1]==y]=x,update(z);//处理x与原爷爷的关系,挂y的地方,挂上x
        fa[x]=z;//此处写错了位置
        fa[y]=x,ch[x][f^1]=y;//此处错写成fa[y]=x,ch[x][f^1]=y,update(x);//处理x与原父亲的关系,挂u的地方,挂上y
        fa[u]=y,ch[y][ch[y][1]==x]=u,update(y);//此处错写成fa[u]=y,ch[y][ch[y][1]==x]=u,update(y);//处理x的子u与原x父亲的关系,挂x的地方,挂上u。
    }
    void update(int x){
        size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
    }
}Tree;
int main(){
    int sum=0,n,Val,i;
    scanf("%d",&n);
    Tree.insert(INF);//因查找过程中,一定要找到一个后继,故手动插入最大值
    Tree.insert(-INF);//因查找过程中,一定要找到一个前驱,故手动插入最小值
    for(i=1;i<=n;i++){
        scanf("%d",&Val);
        if(i==1)sum+=Val;
        else sum+=Tree.Query(Val);
        Tree.insert(Val);//别忘了,插入该节点
    }
    printf("%d\n",sum);
    return 0;
}

你可能感兴趣的:(跟着大佬学算法)