hdu5192 树状数组

具体解法bestcoder上都有,这里就不详细写了。

主要用到一个树状数组区间更新单点求和的问题,以前这类问题都是用线段树做的,这次研究了一下树状数组。

大概就是,记录的值为每一个点和之前一个点的差值,这样的话,以前的函数依然可以用,但是求和函数的意义发生变化。之前求和求得的是区间和,但是现在的记录方式,求得的是最后一个节点相对于0节点的变化量总和,如果0节点值恒为0,那么这样求的便是最后一个节点的值,即变成了单点查询。

附代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <stack>
#include <queue>
#include <map>
#include <algorithm>
#define ll long long int
#define ull unsigned long long int
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define delf int m=(l+r)>>1

using namespace std;

ll max(int a,int b)
{
    return a>b?a:b;
}

ll min(int a,int b)
{
    return a>b?b:a;
}

ll n,w,h;
ll v[150050];
ll dp[2][150050];
ll sum;
ll ss;
ll ans;
ll H;
ll s1[50050];      //维护个数,区间更新,单点查询
ll s2[50050];      //维护总和,单点更新,区间查询

int lowbit(int x)
{
    return x&(-x);
}

void add1(int x,int v)       //x与之前一个位置的差值+1
{
    while (x<=50000)
    {
        s1[x]+=v;
        x+=lowbit(x);
    }
    return ;
}

void add2(int x,int v)      //单点更新
{
    while (x<=50000)
    {
        s2[x]+=v;
        x+=lowbit(x);
    }
    return ;
}

int gets1(int x)            //单点查询
{

    int s=0;
    while (x>0)
    {
        s+=s1[x];
        x-=lowbit(x);
    }
    return s;
}

int gets2(int x)            //区间查询
{
    int s=0;
    while (x>0)
    {
        s+=s2[x];
        x-=lowbit(x);
    }
    return s;
}

void solve(ll h1)
{
    //ss1指小于h1的个数,cc1指小于h1的元素总和,ss2和cc2同理
    ll ss1=gets1(h1-1);
    ll ss2=w-ss1;
    ll cc1=gets2(h1-1);
    ll cc2=sum-cc1;
    ll c=max(ss1*h1-cc1,cc2-ss2*h1);
    if (c<ans)
    {
        ans=c;
        H=h1;
    }
    if (c==ans&&H<h1&&h1*w<=ss)     //消耗相同的情况下,取高度大的
    {
        ans=c;
        H=h1;
    }
    return ;
}

int main()
{
    while (~scanf("%I64d%I64d%I64d",&n,&w,&h))
    {
        h+=1;
        for (int i=0;i<w;i++)
            v[i]=v[i+n+w]=1;
        sum=0;
        for (int i=w;i<=n+w-1;i++)
        {
            scanf("%d",&v[i]);
            v[i]++;     //将每堆积木的增加一个,这样两端之前没有的积木高度都变为1,才可以用树状数组进行记录。
            sum+=v[i];
        }
        ss=sum;
        if (sum<w*h)
        {
            printf("-1\n");
            continue ;
        }
        ans=(ll)w*h;
        H=h;
        sum=w;
        add1(1,w);
        add2(1,w);
        for (int i=w;i<=n+w*2-1;i++)
        {
            int p=v[i-w];
            sum-=p;
            add1(p,-1);
            add2(p,-p);
            p=v[i];
            sum+=p;
            add1(p,1);
            add2(p,p);
            if (sum/w>=h)
                solve(sum/w);
            if (sum/w+1>=h)
                solve(sum/w+1);
            solve(h);
        }
        add1(1,-w);
        add2(1,-w);
        printf("%I64d %I64d\n",H-1,ans);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值