Hdu 5192 Building Blocks Ⅱ


Building Blocks Ⅱ

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 245    Accepted Submission(s): 54


Problem Description
LeLe is playing with blocks again.He wants to rebuild the blocks which he built yesterday. 

LeLe wants to build piles which contains consecutive  W  piles with the same height and the height is not shorter than  H .

LeLe already put all of his blocks in these piles, which means he can not add any blocks into them.Besides, he can move a block from one pile to another or a new one,but not the position betweens two piles already exists.For instance,after one move,"3 2 3" can become "2 2 4" or "3 2 2 1",but not "3 1 1 3".

You are request to calculate the minimum blocks should LeLe move and the height.
 

Input
There are multiple test cases, about  100  cases.

The first line of input contains three integers  n,W,H(1n,W,H50000) . n  indicate  n  piles blocks.

For the next line ,there are  n  integers  A1,A2,A3,,An  indicate the height of each piles. (1Ai50000) .

The height of a block is 1.
 

Output
For each case, output two integers,the first one is  h  (indicate the height of W piles),the second one is the minimum number of blocks should LeLe move.

If there are multiple solutions output the maximal  h .

If there is no solution, output "-1" (without quotes).
 

Sample Input
  
  
3 3 2 4 2 4 4 3 4 6 6 3 10 4 4 4 1 2 3 4
 

Sample Output
  
  
3 2 5 2 -1
Hint
In first case, LeLe moves one block from first pile to second pile and move one block from third pile to the right (out of three piles).The number of piles become 3 3 3 1, The minimum step is two. In second case, LeLe moves a block from first pile and second pile to third pile.
 

Source

题意:点击打开链接

题解:思路:枚举区间,然后求解每个区间的最优值。

关键在于,对于一个给定的区间,如何求最优的h值。

对于确定的区间,假设最终的高度为h,
代价是
    
    
     
     max((Hih),(hHj))Hih,Hjh)
    
    
等价于
    
    
     
     max(Hicnt(i)h,cnt(j)hHj)
    
    
(
    
    
     
     cnt(i)
    
    表示满足
    
    
     
     Hih
    
    的堆数, 
    
    
     
     cnt(j)
    
    表示满足
    
    
     
     Hjh
    
     的堆数)。
    
    
     
     Hicnt(i)h
    
    关于h呈递减,
    
    
     
     cnt(j)hHj
    
    关于h呈递增。一个递减到0,一个从0开始递增,所以代价与h的函数图像是V字形的,交点处代价最小。此时 
    
    
     
     Hicnt(i)h=cnt(j)hHjh=Hi+Hjcnt(i)+cnt(j)
    
    ,分母是总堆数W,分子是这个区间积木的总个数。h实际上就是这个区间的平均高度aver。考虑到四舍五入,答案是aver或者aver+1,当然还需要与题目给定的H做下比较,最终的方案是这3个数之一。
那么现在的问题的是对于给定的h,如何求花费。即,求大于等于h的数的个数跟和,小于h的数的个数跟和。按值建树,用线段树或树状数组维护即可。
复杂度O((n+w)*logn)
我是用树状数维护的,代码如下:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<queue>
#include<math.h>
#include<stack>
#include<map>
#include<set>
#include<stdlib.h>
#include<vector>
#define inff 0x3fffffff
#define nn 110000
#define mod 1000000007
#define eps 1e-8
typedef __int64 LL;
typedef unsigned __int64 LLU;
const LL inf64=inff*(LL)inff;
using namespace std;
int n;
LL w,H;
int a[nn];
LL ans;
int id;
LL Tree[nn];
int num[nn];
LL sum;
void init()
{
    memset(num,0,sizeof(num));
    memset(Tree,0,sizeof(Tree));
    memset(a,0,sizeof(a));
    ans=inf64;
    sum=0;
}
int inline lowbit(int x)
{
    return x&(-x);
}
void add(int x,int val)
{
    if(x==0)
        return ;
    for(int i=x;i<=50000;i+=lowbit(i))
    {
        Tree[i]+=val;
    }
}
void ad(int x,int val)
{
    x++;
    for(int i=x;i<=50001;i+=lowbit(i))
    {
        num[i]+=val;
    }
}
LL getn(int x)
{
    x++;
    int re=0;
    for(int i=x;i;i-=lowbit(i))
    {
        re+=num[i];
    }
    return re;
}
LL get(int x)
{
    LL re=0;
    for(int i=x;i;i-=lowbit(i))
    {
        re+=Tree[i];
    }
    return re;
}
void qiu(int x)
{
    if(x>50000)
        return ;
    if(x*w>sum)
        return ;
    LL ix=max(getn(x)*x-get(x),get(50000)-get(x)-(getn(50000)-getn(x))*x);
    if(ix<ans)
    {
        ans=ix;
        id=x;
    }
    else if(ix==ans)
    {
        id=max(id,x);
    }
}
void jie()
{
    int ix=get(50000)/w;
    if(H>ix)
    {
        qiu(H);
    }
    else
    {
        qiu(ix);
        qiu(ix+1);
    }
}
void solve()
{
    int i;
    for(i=1;i<=w;i++)
    {
        add(a[i],a[i]);
        ad(a[i],1);
    }
    jie();
    for(i=w+1;;i++)
    {

        ad(a[i-w],-1);
        add(a[i-w],-a[i-w]);
        add(a[i],a[i]);
        ad(a[i],1);
        jie();
        if(i-w>=n)
            break;
    }
}
int main()
{
    int i;
    while(scanf("%d%I64d%I64d",&n,&w,&H)!=EOF)
    {
        init();
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        if(sum<w*H)
        {
            puts("-1");
            continue;
        }
        solve();
        for(i=1;i<=n/2;i++)
        {
            swap(a[i],a[n-i+1]);
        }
        memset(Tree,0,sizeof(Tree));
        memset(num,0,sizeof(num));
        solve();
        printf("%d %I64d\n",id,ans);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值