Codeforces Round #583 F Employment

原题

https://codeforces.com/contest/1214/problem/F

题目大意

有m座城市围成一个圈。某公司在其中n座城市中各有一个空的办公室;而又恰有n个员工,他们的家位于n个城市中。给出办公地点所在城市与员工所在城市,问如何规划每个员工的上班地点,才能使得他们上班的总距离最小。这些城市有可能相同。

数据规模:\(m \le 10^9\) \(n \le 2 \times 10^5\)

题解

这就是一道智商题,不需要任何算法,但细节不容易处理。

首先不难想到的是O(n^2)的做法。我们将所有办公城市与居住城市从小到大排列,这样一来无论工作地点如何分配,其相对顺序都不会改变。因此只需要将第一个员工逐个尝试放置于不同城市中工作,再统计出其他员工的上班总距离即可。

考虑如何优化时间复杂度。我们令ans[i]表示第一个员工向右移动i个城市时的上班总距离,move(i, j)表示居住在第i个城市的员工假如要到第j个城市上班,则相当于向右移动了多少个城市,dis(x[i], y[j])表示第i个员工到第j个城市工作的上班距离,则题目要求的本质上是以下式子:
\[ ans[move(i, j)] = \sum_i \sum_j dis(x[i], y[j]) \]

尝试分类讨论写出对于某一个i,dis(x[i], y[i])的表达式:
\[ dis(x[i], y[j])=\left\{\begin{array}{}-x[i] + m - y[j] \space(2 \cdot y[j] < 2 \cdot x[i] - m) \\x[i] - y[j] \space(2 \cdot x[i] + m \le 2 \cdot y[j] < 2 \cdot x[i]) \\- x[i] + y[j] \space (2 \cdot x[i] \le 2 \cdot y[j] < 2 \cdot x[i] + m) \\x[i] + m - y[j] \space (2 \cdot y[j] \ge x \cdot x[i] + m)\end{array}\right. \]
注意到这个表达式分为四段。分段点y[j]满足随着x[i]递增而递增,因此我们可以将从小到大遍历一遍,三个分段点也就从左到右移动一遍,每到达一个i,我们按照分段点得到的区间,对于一整段的j,在ans[move(i, j)]中整段加上\(a \cdot x[i] + b \cdot m\)(用前缀和维护)。至于剩下y[j]的项怎么处理?我们注意到,对于每一个y[i], dis(x[j], y[i])同样有一个分为四类的表达式,因此我们按照y[i]再遍历一遍,即可在ans中加完所有项。

必须要注意:在dis(x[i], y[j])的表达式中,可能存在x[i]和y[j]正好在圆圈的一条直径上的情况,即从任意一侧上班,距离都是相等的。这种情况下,在遍历x[i]和y[i]时统一表达式,否则就会出错。x[i]=y[j]的情况同理,要注意是哪个减哪个。

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
inline int getInt()
{
    int a = 0, sgn = 1; char c;
    while (! isdigit(c = getchar())) if (c == '-') sgn *= -1;
    while (isdigit(c)) a = a * 10 + c - '0', c = getchar();
    return a * sgn;
}
const int N = 10 + 2e5;
int m, n;
struct pr
{
    int pos, id;
}x[N], y[N];
inline bool comp(pr a, pr b) {return a.pos < b.pos;}
long long pre[N];   //[i]表示往右移动i位
int op[N];
inline void modify1(int p, int L, int R, int a)
{
    if (R < p) pre[L + n - p] += a, pre[R + n - p + 1] -= a;
    else if (L < p && R >= p)
    {
        pre[L + n - p] += a; pre[n] -= a;
        pre[0] += a; pre[R - p + 1] -= a;
    }
    else if (L >= p) pre[L - p] += a, pre[R - p + 1] -= a;
}
inline void modify2(int p, int L, int R, int a)
{
    if (R <= p) swap(L, R), pre[p - L] += a, pre[p - R + 1] -= a;
    else if (L <= p && R > p)
    {
        pre[0] += a; pre[p - L + 1] -= a;
        pre[p + n - R] += a; pre[n] -= a;
    }
    else if (L > p) swap(L, R), pre[p + n - L] += a, pre[p + n - R + 1] -= a;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("F.in", "r", stdin);
    freopen("F.out", "w", stdout);
#endif
    m = getInt(); n = getInt();
    for (int i = 1; i <= n; i ++) y[i].pos = getInt(), y[i].id = i;
    for (int i = 1; i <= n; i ++) x[i].pos = getInt(), x[i].id = i;
    sort(x + 1, x + n + 1, comp); sort(y + 1, y + n + 1, comp);
    int p1 = 1, p2 = 1, p3 = 1;
    memset(pre, 0, sizeof pre);
    for (int i = 1; i <= n; i ++)
    {
        while (p1 <= n && (long long)2 * y[p1].pos < 2 * (long long)x[i].pos - m) p1 ++;
        while (p2 <= n && y[p2].pos < x[i].pos) p2 ++;
        while (p3 <= n && (long long)2 * y[p3].pos < 2 * (long long)x[i].pos + m) p3 ++;
        if (p1 > 1) modify1(i, 1, p1 - 1, - x[i].pos + m);
        if (p1 < p2) modify1(i, p1, p2 - 1, x[i].pos);
        if (p2 < p3) modify1(i, p2, p3 - 1, -x[i].pos);
        if (p3 <= n) modify1(i, p3, n, x[i].pos + m);
    }
    p1 = 1; p2 = 1; p3 = 1;
    for (int i = 1; i <= n; i ++)
    {
        while (p1 <= n && (long long)2 * x[p1].pos <= 2 * (long long)y[i].pos - m) p1 ++;
        while (p2 <= n && x[p2].pos <= y[i].pos) p2 ++;
        while (p3 <= n && (long long)2 * x[p3].pos <= 2 * (long long)y[i].pos + m) p3 ++;
        if (p1 > 1) modify2(i, 1, p1 - 1, -y[i].pos);
        if (p1 < p2) modify2(i, p1, p2 - 1, y[i].pos);
        if (p2 < p3) modify2(i, p2, p3 - 1, -y[i].pos);
        if (p3 <= n) modify2(i, p3, n, y[i].pos);
    }
    long long ans = 1e15; int mv;
    for (int i = 0; i < n; i ++)
    {
        pre[i] += pre[i - 1];
        if (pre[i] < ans) ans = pre[i], mv = i;
    }
    printf("%lld\n", ans);
    for (int i = 1; i <= n; i++)
        if (i > mv) op[y[i].id] = x[i - mv].id; else op[y[i].id] = x[i + n - mv].id;
    for (int i = 1; i <= n; i++) printf("%d ", op[i]);
}

猜你喜欢

转载自www.cnblogs.com/ZeonfaiHo/p/11502617.html