You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

P2345 奶牛集会

一、题目描述

约翰的N头奶牛每年都会参加 哞哞大会。哞哞大会是奶牛界的盛事。集会上的活动很多,比如堆干草,跨栅栏,摸牛仔的屁股等等。它们参加活动时会聚在一起,第i头奶牛的坐标为X_i,没有两头奶牛的坐标是相同的。奶牛们的叫声很大,第i头和第j头奶牛交流,会发出max(V_i,V_j) × |X_iX_j| 的音量,其中V_iV_j 分别是第i 头和第j,头奶牛的听力。假设每对奶牛之间同时都在说话,请计算所有奶牛产生的音量之和是多少。

Input 第一行:单个整数N

第二行到第N+1 行:第i+1 行有两个整数V_iX_i

Output 单个整数:表示所有奶牛产生的音量之和

Sample Input

4
3 1
2 5
2 6
4 3

Sample Output

57

Hint 所有数据≤ 20000

二、解题思路

首先分析一下式子max\{V_i,V_j\}×|X_i X_j|

想办法化简公式,去掉原公式中的max和绝对值符号。

  1. 将每头奶牛按听力V_i由小到大进行排序,那么当走到第i头牛时,它与前i-1头牛的\displaystyle \large \max_{j<i}(V_i,V_j)=V_i,这样就成功去掉了最左侧的max\{V_i,V_j\}
  2. |X_i X_j|这个分两种情况:
    • X_i>X_j,转换为X_i-X_j
    • X_i<X_j,转换为X_j-X_i
  3. \large \displaystyle \sum max\{V_i,V_j\}×|X_i X_j|
    • i头牛的v_i * (它之前牛的个数 * 它的坐标 - 它之前所有牛的坐标和)
    • i头牛的v_i * (它之后所有牛的坐标和 - 它之后牛的个数 * 它的坐标)

i头牛和其他的牛发出的声音 = ① + ②

对于牛在某个坐标区间内的个数和牛的坐标和,我们可以用两个树状数组来维护。

三、Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 20010;

// 听力值v,坐标x

// 结构体+第一维、第二维由小到大排序
struct Node {
    int v, x;
    const bool operator<(const Node &t) const {
        if (v == t.v) return x < t.x;
        return v < t.v;
    }
} a[N];

// 树状数组模板
int c1[N], c2[N];

#define lowbit(x) (x & -x)

void add(int c[], int x, int v) {
    while (x < N) c[x] += v, x += lowbit(x);
}

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

int main() {
#ifndef ONLINE_JUDGE
    freopen("P2345.in", "r", stdin);
#endif

    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d %d", &a[i].v, &a[i].x);

    sort(a + 1, a + n + 1); // 排序

    LL res = 0;
    for (int i = 1; i <= n; i++) {
        // c1:坐标和树状数组
        // c2:牛的个数
        LL s1 = sum(c1, a[i].x - 1);              // a[i]进入树状数组时,它前面所有牛的坐标和
        LL s2 = sum(c1, 20000) - sum(c1, a[i].x); // a[i]进入树状数组时,它后面所有牛的坐标和

        LL cnt = sum(c2, a[i].x);
        /*
        cnt:它之前牛的个数
        i-1-cnt:它之后牛的个数
        a[i].x它的坐标
        */

        // 方法1cout << "i=" << i << ",sum(c2,N)=" << sum(c2, N) << endl;
        res += a[i].v * (cnt * a[i].x - s1 + s2 - (sum(c2, N) - cnt) * a[i].x);

        //  方法2
        // res += a[i].v * (cnt * a[i].x - s1 + s2 - (i - 1 - cnt) * a[i].x);

        add(c1, a[i].x, a[i].x); // 维护坐标前缀和
        add(c2, a[i].x, 1);      // 维护个数前缀和
    }
    // 输出结果
    printf("%lld\n", res);
    return 0;
}