问题描述
乐乐又开始搭积木了。 他想在昨天搭完的积木上,重新搭建,使得其中有连续W堆积木具有相同的高度,同时他希望高度最少为H。 乐乐的积木都这了,也就是说不能添加新的积木,只能移动现有的积木。 他可以把一个积木从一堆移动到另一堆或者新的一堆,但是不能移动到两堆之间。比如,一次移动之后,"3 2 3" 可以变成 "2 2 4" 或者 "3 2 2 1",但是不能变成"3 1 1 3". 请你帮他算算,当搭建的高度h为多少时,需要移动的积木最少,如果有多个h满足条件,输出h的最大值。
输入描述
有多组数据,大约100组。 对于每组数据,第一行三个整数n、W、H。 第二行n个元素,表示n座积木的高度。 题目中所有数据的范围[1,50000];
输出描述
输出两个整数,第一个数表示搭建的高度h,第二个数表示需要移动的最小积木数。 如果有多个h满足使得移动步数最少,输出h的最大值。 如果不能形成高度最少为H的连续的W堆积木,输出-1。
输入样例
3 3 2 4 2 4 4 3 4 6 6 3 10 4 4 4 1 2 3 4
输出样例
3 2 5 2 -1
Hint
样例一解释,一种可行的方案是从第一堆移动一个到第二堆,从第三堆上面移动一个放到右边,每堆个数变成 3 3 3 1。最少移动2次。 样例二解释,把第一座和第二座积木上的一个积木移动到第三座积木上,得到3*5。
思路:大致想法和5191类似。但每个区间的h是不同的。比较容易想到,合适的h是这个区间的平均数(如果h<给定的下限hl,则为hl)。但是朴素的方法,在算每个区间小于h和大于h的和时,会是O(n^2)的,肯定不行,因此我们需要用到树状数组维护。维护的方法,对于树状数组用的少人(比如我...)来说不好想到...其实这里维护的是所有高度的可能值,而不是数组序列。这种方法类似于用树状数组维护逆序对个数(具体方法详见我另外的博文),另外也用到区间修改,查询点的思想(即加一个,减一个)...我这里用了两个树状数组维护区间内,<=h的元素个数和<=h的元素的总和,再通过区间内总共有w个元素且区间内元素总和,算出大于h的这两个值。
注意:(1)每次的平均数不能直接四舍五入...两边都要算一下!!(被这个点WA了一晚上加一早上...)
(2)树状数组中的n要注意是maxh!别随手一打就写成n了....
(3)h还有上界,算平均数上取整的时候是有可能超过这个上界的!
代码中间有些调整,导致写的有些乱...不要介意。。。
#include <iostream>
#include <cmath>
#include <stdio.h>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdlib>
#include <map>
using namespace std;
#define I64_MAX 9223372036854775807
typedef long long ll;
const double pi=acos (-1.0);
const long double eps=1e-15 ;
const ll INF=(I64_MAX)/2;
//#pragma comment(linker, "/STACK:102400000,102400000")
const int inf=0x3f3f3f3f ;
#define maxx(a) memset(a, 0x3f, sizeof(a))
#define zero(a) memset(a, 0, sizeof(a))
#define FILL(a,b) memset(a, b, sizeof(a))
#define REP(i,a,b) for(i=a;i<b;i++)
#define rep(i,n) REP(i,0,n)
#define srep(i,n) for(i = 1;i <= n;i ++)
#define snuke(c,itr) for( __typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)
#define MP make_pair
#define fi first
#define se second
typedef pair <int, int> PII;
typedef pair <ll, ll> PX;
typedef pair<int,ll> PIL;
#define MAX 200000
const int maxh = 50005;
int n,m,w;
ll hl,hh;
int a[MAX];
ll tree[2][maxh];
ll qian[MAX];
inline int lowbit(int x)
{
return x&(-x);
}
void update(int i,ll x,int j)
{
//因为索引不能有0
i++;
while(i <= maxh)
{
tree[j][i] += x;
i += lowbit(i);
}
}
ll query(int i,int j)
{
i++;
ll ret = 0;
while(i>0)
{
ret += tree[j][i];
i -= lowbit(i);
}
return ret;
}
int main ()
{
//freopen("E:\\in.txt" ,"r", stdin);
// freopen ("E:\\out.txt","w",stdout);
int i,j;
while(scanf("%d%d%lld",&n,&w,&hl)==3)
{
ll sum = 0;
memset(tree,0,sizeof(tree));
memset(qian,0,sizeof(qian));
memset(a,0,sizeof(a));
for(i=w;i<w+n;i++)
{
scanf("%d",&a[i]);
sum += (ll)a[i];
}
if(sum < (ll)w*hl)
{
puts("-1");
continue;
}
hh = sum / (ll)w;
n = w+w+n;
sum = 0;
for(i = w;i<n;i++)
{
sum -= (ll)a[i-w];
sum += (ll)a[i];
qian[i] = sum;
}
ll ans = (ll)w*hl;
ll ansh = hl;
ll high,low;
update(0,(ll)w,0);
for(i=w;i<n;i++)
{
update(a[i-w],(ll)-1,0);
update(a[i],(ll)1,0);
update(a[i-w],(ll)-a[i-w],1);
update(a[i],(ll)a[i],1);
ll h1 = (ll)((long double)qian[i] / (long double)w + eps);
h1 = max(hl,h1);
h1 = min(h1,hh);
ll tmp1 = query((int)h1,0);
ll tmp2 = query((int)h1,1);
low = tmp1*h1 - tmp2;
high = qian[i] - tmp2 - (w - tmp1) * h1;
low = max(low,high);
if(low < ans)
{
ans = low;
ansh = h1;
}
else if(low == ans && h1 > ansh)
{
ansh = h1;
}
h1 = (ll)((long double)qian[i] / (long double)w + eps) + 1;
h1 = max(hl,h1);
h1 = min(h1,hh);
tmp1 = query((int)h1,0);
tmp2 = query((int)h1,1);
low = tmp1*h1 - tmp2;
high = qian[i] - tmp2 - (w - tmp1) * h1;
low = max(low,high);
if(low < ans)
{
ans = low;
ansh = h1;
}
else if(low == ans && h1 > ansh)
{
ansh = h1;
}
}
printf("%lld %lld\n",ansh,ans);
}
return 0;
}