!-- Loading 底层遮罩 -->

P2345 奶牛集会

感谢所有AC

 

传送门

思路

       题目的第一思路应当是减少重复计算从而降低时间开销。对每一头奶牛来说,需要计算出它与所有听力比它差的奶牛对答案的贡献。对于一头奶牛,如何快速筛出听力比它差的所有奶牛?由此推出需要对听力先进行一个排序,得到序列只要对每一个听力计算贡献即可。

       接着剖析公式,可以得知,对于序列中每一头奶牛来说,其对答案的贡献为 v * [sum1 + (num2 - num1) * x - sum2] 。如何在不产生大量重复计算的情况下统计全部贡献?我们希望对于每一头奶牛,不需要每次都把sum1/2,num1/2进行计算,尽可能的前一个计算结果可以为下一个计算过程所用。那么这样的需求就需要满足序列中 x 也按顺序排列,这样在计算新的一头奶牛时只需要在前面的计算结果上面稍作修改就可以直接运算。

       但是显然这是不现实的,在 x 顺序排列下 v 的顺序结构会遭到破坏,所以只能退而求其次,是否存在 x 有序而 v 相对有序的情况(指对于现在计算的奶牛来说,它计算的贡献都是听力比它差的,且它的x值小于下一头要计算的奶牛的x值)?在这样的情况下,cdq分治浮出水面,是否可以把序列分成两个部分,计算后一部分对答案的贡献?

       而对于 x 有序而 v 相对有序的需求,可以这样考虑,在分治中的一个序列,x 完全有序,而前一部分的 v 值小于后一部分的任何 v 值,在这样的情况下就可以对奶牛进行上述公式的运算,计算后一部分在前一部分奶牛上的贡献,如此进行分治就可以把所有奶牛的贡献都求出来。想要得到这么一个序列,可以考虑归并的性质(因为要完成这样的序列需要把计算完成后的两个小序列进行合并才能让大的序列满足这样的性质)。

代码

#include<iostream>
#include<algorithm>
#define maxn 20007
using namespace std;
struct Node {
	int v, x;
	bool operator <(const Node& A)const {
		if (v != A.v)return v < A.v;
		return x < A.x;
	}
}a[maxn],temp[maxn];
int n; long long ans;
void cdq(int l, int r) {
	if (l == r)return;
	int mid = (l + r) >> 1;
	cdq(l, mid), cdq(mid + 1, r);
	long long sum1 = 0, sum2 = 0;
	for (int i = l; i <= mid; i++)
		sum1 += a[i].x;
	for (int i = l, j = l, k = mid + 1; k <= r; k++)
	{
		while (a[i].x < a[k].x && i <= mid) {
			sum1 -= a[i].x, sum2 += a[i].x;
			temp[j++] = a[i++];
		}
		long long num1 = mid + 1 - i, num2 = i - l;
		ans += a[k].v * (sum1 + (num2 - num1) * a[k].x - sum2);
		temp[j++] = a[k];
		while (k == r && i <= mid)
			temp[j++] = a[i++];
	}
	for (int i = l; i <= r; i++)
		a[i] = temp[i];
}
int main(void)
{
	ios::sync_with_stdio(false);
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i].v >> a[i].x;
	sort(a + 1, a + n + 1);
	cdq(1, n);
	cout << ans << endl;
	return 0;
}
 

 

posted @ 2022-03-30 20:44  Thinker-X  阅读(33)  评论(0编辑  收藏  举报