ACM暑假训练 问题 G: Balanced Photo (树状数组优化)

问题 G: Balanced Photo

时间限制: 1 Sec   内存限制: 128 MB
提交: 67   解决: 17
[ 提交][ 状态][ 讨论版]

题目描述

Farmer John is arranging his N cows in a line to take a photo (1N100,000). The height of the ith cow in sequence ishi, and the heights of all cows are distinct. 
As with all photographs of his cows, FJ wants this one to come out looking as nice as possible. He decides that cow i looks "unbalanced" if Li and Ri differ by more than factor of 2, where Li and Ri are the number of cows taller than i on her left and right, respectively. That is, i is unbalanced if the larger of Li and Ri is strictly more than twice the smaller of these two numbers. FJ is hoping that not too many of his cows are unbalanced.

Please help FJ compute the total number of unbalanced cows.

输入

The first line of input contains  N . The next  N  lines contain  h1hN , each a nonnegative integer at most 1,000,000,000.

输出

Please output a count of the number of cows that are unbalanced.

样例输入

7
34
6
23
0
5
99
2

样例输出

3

提示

In this example, the cows of heights 34, 5, and 2 are unbalanced.


【解析】:

十万的数据量,两层循环必定超时。


先给这个数列排排名次,不妨让最小的当第一名,最高的当最后一名,

这样得到一个排名的数列,而且不会改变原数列的前后之间的大小关系。不妨令此数列为数组d


有了数列 d,我们从左往右扫一遍数列 d。另外申请一个数组 c 做辅助。


c[ i ] 表示i左边比 d[ i ] 小的数目。


扫到d[ i ] 的时候用temp=c[ i ] 得到当前i的前面有多少个比d[ i ] 小的数。再用i-temp+1即得到了左边的大数的数目。

然后让所有的小于d[i]的 j ,使得c[ j ]加1。表示比d[i]小的数目全都加1

与此同时,记录下i左边的大的数,存入L[ i ];


代码说一下就是:

for(int i=1;i<=n;i++)
{	
	int temp=c[i];//i左边的矮牛数
	L[i]=i-temp-1;//i左边的高牛数
	for(int j=1;j<=d[i];j++)
		c[j]++;
}

但是这样必定超时,所以数组c用树状数组来维护,就可以避免内层for循环。复杂度从O(n^2)降到O(n*log(n));


如上,左边的高牛数完了,把数列倒过来再数一遍得到R[ i ]。


【代码】:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n,c[520000],a[520000];
int t[520000],d[520000];
int L[520000],R[520000];
bool cmp(int x,int y){
	return a[x]<a[y];
}
void add(int k,int num)
{
	while(k<=n){
		c[k]+=num;
		k+=k&-k;
	}
}
int find(int k)
{
	int sum=0;
	while(k){
		sum+=c[k];
		k-=k&-k;
	}
	return sum;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		t[i]=i;
	}
	sort(t+1,t+1+n,cmp);
	for(int i=1;i<=n;i++)
	{
		d[t[i]]=i;//身高序号
	}
	//数组d把原序列按大小分配序号,但原序列不变 
	
	for(int i=1;i<=n;i++)//从左数牛 
	{
		int temp=find(d[i]);//i左边的矮牛数
		L[i]=i-temp-1;//i左边的高牛数
		add(d[i],1);
	}
	memset(c,0,sizeof(c));//树状数组清0 
	for(int i=n;i>=1;i--)//从右边数得到右边,原理同左 
	{
		int temp=find(d[i]);//i右边的矮数目 
		R[i]=n-i-temp;  //i右边的高牛数目 
		add(d[i],1);
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(2*min(L[i],R[i])<max(L[i],R[i]))
			ans++;
	}
	cout<<ans<<endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪的期许

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值