【题解】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\)头奶牛前的奶牛分成两类:
-
\(j < i, x_{j} < x_{i}\)
-
\(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