P2234 [HNOI2002]营业额统计
链接戳这里☞
上代码(附有注释):
#include<bits/stdc++.h>
using namespace std;
struct sd
{
int value;//当前节点所储存的值
int son[2];//左右儿子
int delta; //暂时没用
int father;//自己的爸爸
int below;//自己下面树的大小
}tree[1000005];
int root;//根
int cnt;//下标分配
int dir;//旋转方向 0是左边 1是右边
inline void maintain(int now)//维护 更新左右子树的大小(这道题中可以不写)
{
tree[now].below=1;
if(tree[now].son[0])tree[now].below+=tree[tree[now].son[0]].below;
if(tree[now].son[1])tree[now].below+=tree[tree[now].son[1]].below;
}
inline int get(int now)//确定旋转方向
{
if(tree[tree[now].father].son[0]==now)return 0;
return 1;
}
//----------------------------------------------------------------------
void rotate(int now)//旋转 需要重点理解
{
dir=get(now);
int fa=tree[now].father,gra=tree[fa].father;
tree[fa].son[dir]=tree[now].son[dir^1];
tree[tree[now].son[dir^1]].father=fa;
if(gra)tree[gra].son[get(fa)]=now;
tree[now].father=gra;
tree[fa].father=now;
tree[now].son[dir^1]=fa;
maintain(fa);
maintain(now);
}
//----------------------------------------------------------------------
inline void splay(int now)
{
int gra,fat;//定义now的祖父和爸爸
int opt1,opt2;//定义两次旋转方向
while(tree[now].father)//如果now还有自己的爸爸,即now还没有旋转到根节点。继续!
{
fat=tree[now].father;gra=tree[fat].father;
if(gra!=0)//如果有祖父,即要进行双旋!
{
opt1=get(now);opt2=get(fat);
if(opt1==opt2){rotate(fat),rotate(now);}
else{rotate(now),rotate(now);}
}
else
rotate(now);
}
root=now;//更新根节点
}
void insertit(int now,int val,int pre)
{
if(!now)//如果now为零(零是假的,其余是真的)说明他没有(左右)儿子,就插入,并且进行根节点旋转
{
cnt++;
tree[cnt].father=pre;
tree[cnt].value=val;
tree[cnt].below=1;
if(tree[pre].value>val)tree[pre].son [0]=cnt;
else tree[pre].son[1]=cnt;
splay(cnt);
}
else//如果now不是零
{
if(tree[now].value>val)insertit(tree[now].son[0],val,now);
else insertit(tree[now].son[1],val,now);
}
}
inline void in(int &x)//快读
{
bool fu=false;
x=0;char c=getchar();
while(!isdigit(c))
{
if(c=='-')
{
fu=true;
}
c=getchar();}
while(isdigit(c)){x*=10,x+=c-48;c=getchar();}
if(fu)x*=-1;
}
int find_pre(int now)//查找前驱
{
if(!now)return -99999999;//如果没有左子树了
if(tree[now].son[1])return find_pre(tree[now].son[1]);
//如果左子树下有右子树,就在继续找右子树的右子树 (上面)
return tree[now].value;
//如果没有右子树了,打出当前值
}
int find_suc(int now)//查找后继
{
if(!now)return 99999999;//如果没有右子树了
if(tree[now].son[0])return find_suc(tree[now].son[0]);
//如果右子树下有左子树,就在继续找左子树的左子树 (上面)
return tree[now].value;
//如果没有左子树了,打出当前值
}
int main()
{
long long tot=0;
int num,prev,sucv;
in(num);
int a;
while(num--)
{
in(a);
insertit(root,a,root);//读入(从当前根向下扔数)
prev=find_pre(tree[root].son[0]);//找前驱 (比他小)
sucv=find_suc(tree[root].son[1]);//找后继(比他大)
if(sucv==99999999&&prev==-99999999)//判定是否为第一个结点
{
tot+=a;
continue;
}
if(a-prev>sucv-a)tot+=sucv-a;//从前驱后继中取小 ,更新总和
else tot+=a-prev;//更新总和
}
printf("%lld",tot);//返回总和
return 0;
}