【BZOJ】1588 [HNOI2002] 营业额统计

Description

营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。 Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况: 该天的最小波动值 当最小波动值越大时,就说明营业情况越不稳定。 而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。 第一天的最小波动值为第一天的营业额。

Input

第一行为正整数 ,表示该公司从成立一直到现在的天数,接下来的n行每行有一个整数(有可能有负数) ,表示第i天公司的营业额。

Output

输出文件仅有一个正整数,即Sigma(每天最小的波动值) 。结果小于2^31 。

Solution

尝试不同的姿势ing~(才不会告诉你们之前拿值域线段树强上失败的故事)
这次是替罪羊树,很暴力很暴力~
估价用的是 log1a(size)<height

#include<stdio.h>
#include<algorithm>
#include<cmath>
#define N 100005

using namespace std;

const double D=log(1/0.75);

int a,rc,rn,cc,ans,op,root,tot,loc[N],num[N],u,n;

struct poi{int a,f,l,r,s,h;}t[N];

void insert(int &k,int fa)
{
    if (!k){t[k=++tot]={a,fa,0,0,1,1};return;}
    if (abs(t[k].a-a)<abs(ans-a)) ans=t[k].a;
    if (a<=t[k].a)
    {
        insert(t[k].l,k);
        if (t[k].l==tot) for (int K=t[k].r;K;K=t[K].l) if (abs(t[K].a-a)<abs(ans-a)) ans=t[K].a;
    }
    else
    {
        insert(t[k].r,k);
        if (t[k].r==tot) for (int K=t[k].l;K;K=t[K].r) if (abs(t[K].a-a)<abs(ans-a)) ans=t[K].a;
    }
    t[k].s++;
    t[k].h=max(t[t[k].l].h,t[t[k].r].h)+1;
    if (log(t[k].s)/D<t[k].h) cc=k;
}

void dfs(const int &k)
{
    if (!k) return;
    loc[++rc]=k;
    dfs(t[k].l);
    num[++rn]=t[k].a;
    dfs(t[k].r);
}

void build(int &k,const int &fa,const int &l,const int &r)
{
    if (r<l) return;
    int mid=l+r>>1;
    t[k=loc[++u]]={num[mid],fa,0,0,r-l+1,0};
    build(t[k].l,k,l,mid-1);
    build(t[k].r,k,mid+1,r);
    t[k].h=max(t[t[k].l].h,t[t[k].r].h)+1;
}

inline void rebuild(int &c)
{
    rc=rn=u=0;
    dfs(c);
    int K=t[c].f;
    build(t[K].l==c?t[K].l:t[K].r,K,1,rc);
    for (;K;K=t[K].f) t[K].h=max(t[t[K].l].h,t[t[K].r].h)+1;
    c=0;
}

int main()
{
    scanf("%d",&n);
    scanf("%d",&a);
    t[0]={0,0,1,1,0,0};
    t[root=tot=1]={op=a,0,0,0,1,1};
    for (int i=1;i<n;i++)
    {
        scanf("%d",&a);ans=233333333;
        insert(root,0);
        op+=abs(ans-a);
        if (cc) rebuild(cc);
    }
    printf("%d\n",op);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值