hdu 5192 Building Blocks Ⅱ && BestCoder Round #34-1003

问题描述
乐乐又开始搭积木了。
他想在昨天搭完的积木上,重新搭建,使得其中有连续
  
  
   
   W
  
  堆积木具有相同的高度,同时他希望高度最少为
  
  
   
   H
  
  。
乐乐的积木都这了,也就是说不能添加新的积木,只能移动现有的积木。
他可以把一个积木从一堆移动到另一堆或者新的一堆,但是不能移动到两堆之间。比如,一次移动之后,"3 2 3" 可以变成 "2 2 4" 或者 "3 2 2 1",但是不能变成"3 1 1 3".
请你帮他算算,当搭建的高度
  
  
   
   h
  
  为多少时,需要移动的积木最少,如果有多个
  
  
   
   h
  
  满足条件,输出
  
  
   
   h
  
  的最大值。 
题解:1.枚举,对于w长的区间进行枚举

  2.对于每段区间,发现如果h = sum(区间的总和) / w时在这段区间上是最优的,因为可以将大于h的部分拿到

小于h的部分。

3.我们需要知道小于高度h的积木的高度和 还有 积木的数量,这些可以通过树状数组在logn的时间复杂度内

  4.比较所有的区间,首先找到最小的移动步数,在移动步数相同的情况下找到最大的高度
        5.贡献两组yyfmaster神的数据,看了他的才改对的
  6 3 1
  2 2 2 1 1 1

  6 3 1
  1 2 3 4 5 6
总结:1.自己想了好久做出来的题目好开心

  2.感觉对于做一个题目,还是要注意规范的想题步骤,这次需要注意的是正方两方面想问题,在h = sum /

 w,这里卡了很久
3.新发现:需要自己找一些好的数据来测试自己的程序,一些比较容易漏想的点在想到时应该给自己出一组数据

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
#define MAXN 50005
#define lowbit(i) (i & -i)
int num[MAXN << 1],n,w,h,lans;
LL total,ans,cur,s[MAXN],prefix[MAXN];
LL sum(int i,LL * a)
{
    LL total = a[0];
    for(;i;i -= lowbit(i))
        total += a[i];
    return total;
}
void modify(int i,LL * a,int key)
{
    if(!i)a[i] += key;
    else for(;i < MAXN;i += lowbit(i))a[i] += key;
}
void findbigger()
{
    int h1 = max((LL)h,cur / w) , h2 = max(cur / w + 1,(LL)h);
    h1 = min((LL)h1,total / w), h2 = min((LL)h2,total / w);
    LL cur1 = sum(h1,prefix),cur2 = sum(h2,prefix);
    int w1 = sum(h1,s),w2 = sum(h2,s);
    LL les1 = w1 * h1 - cur1;
    LL les2 = w2 * h2 - cur2;
    LL more1 = (cur - cur1) - (w - w1) * h1;
    LL more2 = (cur - cur2) - (w - w2) * h2;
    LL a = max(les1,more1),b = max(les2,more2);
    if(ans > min(a,b))
    {
        ans = min(a,b);
        lans = a < b ? h1 : h2;
    }
    else if(ans == min(a,b))
        lans = max(lans,a < b ? h1 : h2);
}
void oper(int i,int neg)
{
    modify(num[i],s,neg);
    modify(num[i],prefix,neg * num[i]);
    cur += neg * num[i];
}
bool solve()
{
    if((LL)w * h > total)return true;
    modify(0,s,w);
    for(int i = 0,j = i - w;i < n + w;i++,j++)
    {
        oper(i,1);
        if(j >= 0)oper(j,-1);
        else modify(0,s,-1);
        findbigger();
    }
    return false;
}
int main()
{
    while(scanf("%d%d%d",&n,&w,&h) != EOF)
    {
        cur = total = 0,ans = 1e9,lans = h;
        memset(prefix,0,sizeof(prefix));
        memset(num,0,sizeof(num));
        memset(s,0,sizeof(s));
        for(int i = 0;i < n;i++)
        {
            scanf("%d",&num[i]);
            total += num[i];
        }
        if(solve())puts("-1");
        else printf("%d %I64d\n",lans,ans);
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值