MooFest POJ 1990 【树状数组】

MooFest

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 60000/30000K (Java/Other)

Problem Description
Every year, Farmer John's N (1 <= N <= 20,000) cows attend "MooFest",a social gathering of cows from around the world. MooFest involves a variety of events including haybale stacking, fence jumping, pin the tail on the farmer, and of course, mooing. When the cows all stand in line for a particular event, they moo so loudly that the roar is practically deafening. After participating in this event year after year, some of the cows have in fact lost a bit of their hearing. 

Each cow i has an associated "hearing" threshold v(i) (in the range 1..20,000). If a cow moos to cow i, she must use a volume of at least v(i) times the distance between the two cows in order to be heard by cow i. If two cows i and j wish to converse, they must speak at a volume level equal to the distance between them times max(v(i),v(j)). 

Suppose each of the N cows is standing in a straight line (each cow at some unique x coordinate in the range 1..20,000), and every pair of cows is carrying on a conversation using the smallest possible volume. 

Compute the sum of all the volumes produced by all N(N-1)/2 pairs of mooing cows. 
 

Input
* Line 1: A single integer, N <br> <br>* Lines 2..N+1: Two integers: the volume threshold and x coordinate for a cow. Line 2 represents the first cow; line 3 represents the second cow; and so on. No two cows will stand at the same location. <br>
 

Output
* Line 1: A single line with a single integer that is the sum of all the volumes of the conversing cows. <br>
 

Sample Input
  
  
4 3 1 2 5 2 6 4 3
 

Sample Output
  
  
57
 

//题意:现在有很多头牛,每头牛有一个位置和一个耳聋程度,每头牛要和另外的所有牛说话(仅1次),每两头牛之间说话的声音必须是两者较大的那个耳聋度,要求距离*声音之和。

//思路:先把所有牛按耳聋程度排序(大->小),每次都选最聋的去和其他牛说话,这样,说话的声音就是这头牛的耳聋度(设为x)。设这头牛是第n个位置,总共有m个位置(输入的位置的最大值),每头牛的位置用dis1、dis2、...、dism表示,设该头牛前面共有num1头牛,后面共有num2头牛。

每头牛和另外所有牛说话的value=x*(dis(n+1)-dis(n)+dis(n+2)-dis(n)+...+dis(m)-dis(n))+x*(dis(n)-dis(1)+dis(n)-dis(2)+...+dis(n)-dis(n-1))=x*((dis(n+1)+dis(n+2)+...+dis(m))-num2*dis(n))+x*(num1*dis(n)-(dis(1)+dis(2)+...+dis(n-1)))

dis(n+1)+dis(n+2)+...+dis(m) 和dis(1)+dis(2)+...+dis(n-1)可以用一个树状数组解

num1、num2也可以用一个树状数组解

所以,这道题要用到2个树状数组!

(1)求num1、num2:

先设一个数组,大小为m。
有牛的位置设为1,没牛的位置设为0。套个树状数组模板(这里不解释了),num1=sum(n-1),num2=sum(m)-sum(n);每头牛结束后把它原来那个位置的值变成为0(即add(dis(n) , -1) ),因为1头牛只要跟其他牛说一次。


(2)求dis(n+1)+dis(n+2)+...+dis(m) 和dis(1)+dis(2)+...+dis(n-1):

再设一个数组,大小为m。
有牛的位置的值设为它的坐标,没牛的位置设为0。套个树状数组模板,dis(n+1)+dis(n+2)+...+dis(m) = sum(m)-sum(n);dis(1)+dis(2)+...+dis(n-1) = sum(n-1);每头牛结束后同样把它原来那个位置的值变成为0(即add(dis(n) , -dis(n)))。

最后用个ans,每次+=value就ok啦!上代码!

值可能非常大,尽量都用__int64,记得输入、输出的时候是%I64d ... 因为这个WA了2次— —!

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
#include <algorithm>
using namespace std;

const int MAX = 20010;

typedef struct {
	__int64 x;
	__int64 num;
}Cow;

int n;
int maxx;

bool cmp(Cow p, Cow q)
{
	return p.num > q.num;
}

__int64 lowbit(__int64 x)
{
	return x&(-x);
}

void add(int k, __int64 x, __int64 c[])
{
	while (k <= maxx)
	{
		c[k] += x;
		k += lowbit(k);
	}
}

__int64 sum(int x, __int64 c[])
{
	__int64 sum = 0;
	while (x > 0)
	{
		sum += c[x];
		x -= lowbit(x);
	}
	return sum;
}

int main()
{
	int i;
	while (scanf("%d", &n) != EOF)
	{
		maxx = 0;
		__int64 c[MAX];// 位置
		__int64 a[MAX];// 0/1
		Cow s[MAX];
		memset(a, 0, sizeof(a));
		memset(c, 0, sizeof(c));
		for (i = 1; i <= n; i++)
		{
			scanf("%I64d%I64d", &s[i].num, &s[i].x);
			if (s[i].x > maxx)
				maxx = s[i].x;
		}
		for (i = 1; i <= n; i++)
		{
			add(s[i].x, 1, a);
			add(s[i].x, s[i].x, c);
		}
		sort(s + 1, s + n + 1, cmp);
		__int64 ans = 0;
		for (i = 1; i <= n; i++)
		{
			//跟后面的牛说话
			__int64 temp1 = s[i].num*((sum(maxx, c) - sum(s[i].x, c)) - (sum(maxx, a) - sum(s[i].x, a))*s[i].x);
			//跟前面的牛说话
			__int64 temp2 = s[i].num*((sum(s[i].x - 1, a))*s[i].x - (sum(s[i].x - 1, c)));
			add(s[i].x, -1, a);
			add(s[i].x, -s[i].x, c);
			ans += (temp1 + temp2);
		}
		printf("%I64d\n", ans);
	}
	return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值