问题描述
乐乐又开始搭积木了。 他想在昨天搭完的积木上,重新搭建,使得其中有连续 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
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);
}
}