Codeforces Round 861 (Div. 2)-B. Playing in a Casino题解

Codeforces Round 861 (Div. 2)-B. Playing in a Casino题解_第1张图片
Codeforces Round 861 (Div. 2)-B. Playing in a Casino题解_第2张图片

【大致题意】

给出 n n n组数据,每组数据有 m m m个元素,让第 i i i组数据的第 j j j个元素与余下每组数据的第 j j j个元素求差的绝对值,将所有差值的和输出。

【题解】

首先需要认识到每组数据中 m m m个元素是相互独立的,我们处理好 m = 1 m=1 m=1的情况就能解决这个问题。

正式解题

(假设 m = 1 m=1 m=1的情况):如果我们想要用求和来简化计算,主要考虑的问题是绝对值,所以我们先对 n n n个数据排序,然后求数据的前缀和存入 s u m sum sum数组,在第i个数据之前的数据一定是小于第i个数据的,之后的一定是大于这个数据的,这时我们只需要用 d a t a i ∗ ( i − 1 ) − s u m [ i − 1 ] + s u m [ n ] − s u m [ i ] − d a t a i ∗ ( n − i ) data_i*(i-1)-sum[i-1]+sum[n]-sum[i]-data_i*(n-i) datai(i1)sum[i1]+sum[n]sum[i]datai(ni),最后要将答案除以 2 2 2,因为有重复计算。

ps:以下是大佬解法,特此感谢(同样是考虑 m = 1 m=1 m=1时)

我们可以将题目看作一个直线上 n n n个点,求每两个点之间的距离之和。首先我们一样进行排序,这时我们要计算每两个点之间的距离时必然会后重复计算的部分,及 d a t a i − 1 data_{i-1} datai1 d a t a i data_i datai之间的距离会被计算 ( n − i − 1 ) + ( i − 1 ) (n-i-1)+(i-1) (ni1)+(i1)次,这样就能轻易的计算出结果了。

【代码如下】

(没写大佬代码,不过肯定比我这简单)

typedef long long ll;
const int N = 3e5 + 5;

vector a[N];
vector v[N];

void solve() {
    ll ans = 0;
    int n, m; scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++)
    {
        a[i].clear(), v[i].clear();
        a[i].push_back(0), v[i].push_back(0);
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            int x; scanf("%d", &x);
            v[j].push_back(x);
        }
    }
    for (int i = 0; i < m; i++) sort(v[i].begin(), v[i].end());
    for (int i = 0; i < m; i++)
        for (int j = 1; j <= n; j++)
            a[i].push_back(v[i][j] + a[i][j - 1]);

    for (int i = 0; i < m; i++) {
        for (int j = 1; j <= n; j++) {
            ans += abs(v[i][j] * (j - 1) - a[i][j - 1]) + abs(v[i][j] * (n - j) - (a[i][n] - a[i][j]));
        }
    }

    printf("%lld\n", ans/2);
}

你可能感兴趣的:(算法)