hdu5192 BC#34 树状数组维护

问题描述
乐乐又开始搭积木了。
他想在昨天搭完的积木上,重新搭建,使得其中有连续W堆积木具有相同的高度,同时他希望高度最少为H。
乐乐的积木都这了,也就是说不能添加新的积木,只能移动现有的积木。
他可以把一个积木从一堆移动到另一堆或者新的一堆,但是不能移动到两堆之间。比如,一次移动之后,"3 2 3" 可以变成 "2 2 4" 或者 "3 2 2 1",但是不能变成"3 1 1 3".
请你帮他算算,当搭建的高度h为多少时,需要移动的积木最少,如果有多个h满足条件,输出h的最大值。 
输入描述
有多组数据,大约100组。
对于每组数据,第一行三个整数nWH。
第二行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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值