其他分享
首页 > 其他分享> > 【题解】P5094 - Moofest

【题解】P5094 - Moofest

作者:互联网

题目大意

题目链接

给定\(n\)头奶牛,已知每头奶牛有其对应的听力和位置坐标。设\(v_{i}\)为第\(i\)头奶牛的听力,\(x_{i}\)为第\(i\)头奶牛的坐标。设\(dis(i, j)\)为第\(i\)头奶牛和第\(j\)头奶牛的坐标之差,则一对奶牛谈话需要的音量为\(max(v_{i}, v_{j}) \times dis(i, j)\)。试求所有的\(n \times (n - 1) \div 2\)头奶牛谈话需要的最小音量总和。

解题思路

显然,通过对算法标签的分析,我们可以知道这道题是一道数据结构题。

题目给出的式子过于繁琐,考虑尽量简化这个式子。显然,\(max(v_{i}, v_{j})\)这一项是比较容易简化的。假设我们将所有的奶牛按听力从小到大排序,那么对于一对奶牛\((i, j)\),在\(i < j\)的情况下,它们交谈所需要的听力显然是\(v_{j}\)。因此,我们可以通过按听力从小到大排序来简化\(max(v_{i}, v_{j})\)这一项。

如果按听力从小到大排序,那么我们在考虑第\(i\)头奶牛对音量总和的贡献时,仅考虑第\(1\)头奶牛至第\(i - 1\)头奶牛与第\(i\)头奶牛谈话的音量,最后也能求出所有\(n \times (n - 1) \div 2\)对奶牛的音量总和。

接下来,我们考虑\(dis(i, j)\)这一项的求解。显然,假设\(i < j\),则对于所有这样一对奶牛\((i, j)\),\(v_{i}\)一定小于\(v_{j}\),但是\(x_{i}\)可能大于等于\(x_{j}\)。因此,我们需要分类讨论,将第\(i\)头奶牛前的奶牛分成两类:

  1. \(j < i, x_{j} < x_{i}\)

  2. \(j < i, x_{j} \geq x_{i}\)

我们可以维护两个信息:第\(i\)头奶牛前,坐标值小于\(x_{i}\)的奶牛的数量;第\(i\)头奶牛前,坐标值小于\(x_{i}\)的奶牛的坐标值之和。设\(t1_{i}\)为坐标值小于\(x_{i}\)的奶牛的数量,\(t2_{i}\)为坐标值小于\(x_{i}\)的奶牛的坐标值之和。

我们可以推算出第\(i\)头奶牛前,坐标值大于等于\(x_{i}\)的奶牛的总数:奶牛总数\(i\) $ - $ 坐标值小于\(x_{i}\)的奶牛总数 $ - $ \(1\)。设\(sum\)为\(\sum\limits_{j = 1}^{i - 1} x_{j}\),则第\(i\)头奶牛前所有坐标值大于等于\(x_{i}\)的奶牛的坐标值之和为\(sum\) $ - $ 坐标值小于\(x_{i}\)的奶牛的坐标值之和。

每次修改时,我们在数据结构\(1\)中\(x_{i}\)的位置加上\(1\),表示这个坐标的奶牛又出现了一次;在数据结构\(2\)中\(x_{i}\)的位置加上\(x_{i}\)。这样,我们每次查询时,只需要查找\(x_{i}\)位置前,数据结构\(1\)的前缀和即可求出坐标值小于等于\(x_{i}\)的奶牛总数,查找数据结构\(2\)的前缀和即为所有坐标值小于等于\(x_{i}\)的奶牛的坐标值之和。

抓住关键词前缀和,我们可以在知识库里检索出有相应特征的数据结构:树状数组。我们用两个树状数组分别维护上文提到的两个信息,即可方便快捷地求出每一头奶牛对音量总和的贡献。

我们设\(cnt\)为第\(i\)头奶牛前坐标值小于\(x_{i}\)的奶牛数量,\(sum\)为第\(i\)头奶牛前坐标值小于\(x_{i}\)的奶牛的坐标值之和,\(pos\)为\(\sum\limits_{j = 1}^{i - 1} x_{j}\)。则第\(i\)头奶牛与第\(i\)头奶牛前坐标值小于\(x_{i}\)的奶牛的距离总和为\(cnt \times x_{i} - sum\),第\(i\)头奶牛与第\(i\)头奶牛前坐标值大于等于\(x_{i}\)的奶牛的距离总和为\((pos - sum) - (i - cnt - 1) \times x_{i}\)。第\(i\)头奶牛的贡献即为\((cnt \times x_{i} - sum) \times v_{i} + \left[(pos - sum) - (i - cnt - 1) \times x_{i}\right] \times v_{i}\)。累加所有奶牛的贡献,就是最终谈话的音量总和。

最后提出一个容易错误的点,如果有同学的样例输出\(53\),说明树状数组的上界没有弄清楚。树状数组更新的位置为\(x_{i}\),即奶牛的坐标。因此,树状数组的上界应该是\(max(x_{i})\)而非\(n\)。

参考代码

在此附上本人的\(AC\)代码,仅供参考,请勿抄袭:

#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 50005
#define int long long

struct node
{
	int voise, pos;
}cow[maxn];

int n, m;
int cnt[maxn], sum[maxn];

bool cmp(node a, node b)
{
	return a.voise < b.voise;
}

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

void add_cnt(int p, int x)
{
	for (int i = p; i <= m; i += lowbit(i))
		cnt[i] += x;
}

int query_cnt(int p)
{
	int Sum = 0;
	for (int i = p; i; i -= lowbit(i))
		Sum += cnt[i];
	return Sum;
}

void add_sum(int p, int x)
{
	for (int i = p; i <= m; i += lowbit(i))
		sum[i] += x;
}

int query_sum(int p)
{
	int Sum = 0;
	for (int i = p; i; i -= lowbit(i))
		Sum += sum[i];
	return Sum;
}

signed main()
{
	int pos_sum = 0, ans = 0;
	scanf("%lld", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld%lld", &cow[i].voise, &cow[i].pos);
		m = max(m, cow[i].pos);
	}
	sort(cow + 1, cow + n + 1, cmp);
	for (int i = 1; i <= n; i++)
	{
		int cnt = query_cnt(cow[i].pos);
		int sum = query_sum(cow[i].pos);
		ans += (cnt * cow[i].pos - sum) * cow[i].voise;
		ans += ((pos_sum - sum) - (i - cnt - 1) * cow[i].pos) * cow[i].voise;
		add_cnt(cow[i].pos, 1);
		add_sum(cow[i].pos, cow[i].pos);
		pos_sum += cow[i].pos;
	}
	printf("%lld\n", ans);
	return 0;
}

这是蒟蒻的第一篇题解,如果有描述不清楚的地方请多多见谅,本人也会继续努力,写出质量更高的题解。

标签:小于,int,题解,sum,times,Moofest,坐标值,奶牛,P5094
来源: https://www.cnblogs.com/Ling-Lover/p/p5094.html