POJ 1990 MooFest 树状数组

题目大意:Farmer John又来恶心我们了!这次他带来了一些牛,这些牛排成一列,他们的位置给出,每一个牛有一个音调。这些牛每两只牛之间都要互相交流,但是交流的时候会有一些花费,i,j两只牛的cost = max(vi,vj) * |posi - posj|。求所有牛之间互相交流的cost和。


思路:一开始我还以为是最大或者最小花费,后来仔细读题发现想多了,就是单纯的统计,但是数据范围2w显然不能n^2的统计,我们需要来想一些优化的方法。

对于所有的牛对来说,最后计算cost的时候是决定于音调高的那一头牛身上。所以就考虑能不能把音调从低到高排序,然后不断的往其中加牛。这样每次的牛加进去的时候,之前的所有牛的声调都比加进去的低,也就可以用新加进去的牛来计算了。

注意到每一个新加进来的牛需要向ans中累加Σvx * |posx - posi|这么多,vx已经知道了,那么怎么求距离之和呢?我的方法是维护4个树状数组,分别统计在x前面的所有点到原点的距离之和,x前面有多少牛;x后面的点的到原点的距离之和,x后面有多少牛。这样就可以通过cnt * posx - Σpos_pred来计算出x前面的距离x之和,后面的也同理。

分析下时间复杂度,O(nlogn),水过吧。。


CODE:

#include 
#include 
#include 
#include 
#define MAX 200010
using namespace std;

struct Complex{
	int val,pos;
	
	bool operator <(const Complex &a)const {
		return val < a.val;
	}
	void Read() {
		scanf("%d%d",&val,&pos);
	}
}point[MAX];

int points;
long long pred[MAX],cnt1[MAX];
long long succ[MAX],cnt2[MAX];

inline void FixPred(int x);
inline void FixSucc(int x);
inline long long GetPred(int x);
inline long long GetSucc(int x);

int main()
{
	cin >> points;
	for(int i = 1;i <= points; ++i)
		point[i].Read();
	sort(point + 1,point + points + 1);
	long long ans = 0;
	for(int i = 1;i <= points; ++i) {
		ans += GetPred(point[i].pos) * point[i].val;
		ans += GetSucc(point[i].pos) * point[i].val;
		FixPred(point[i].pos);
		FixSucc(point[i].pos);
	}
	cout << ans << endl;
	return 0;
}

inline void FixPred(int x)
{
	for(int i = x;i < MAX;i += i&-i) {
		pred[i] += x;
		++cnt1[i];
	}
}

inline void FixSucc(int x)
{
	for(int i = x;i;i -= i&-i) {
		succ[i] += x;
		++cnt2[i];
	}
}

inline long long GetPred(int x)
{
	long long re = 0,cnt = 0;
	for(int i = x;i;i -= i&-i)
		re += pred[i],cnt += cnt1[i];
	return cnt * x - re;
}

inline long long GetSucc(int x)
{
	long long re = 0,cnt = 0;
	for(int i = x;i < MAX;i += i&-i)
		re += succ[i],cnt += cnt2[i];
	return re - cnt * x;
}


你可能感兴趣的:(POJ)