抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

题目传送门

题意

有一个大小为 \(m\) 的环,还上有 \(n\) 个点有工作单位,并且有 \(n\) 个点上住着一个人。你需要为每个人分配一个工作单位,使得所有人到工作单位的距离之和最短。输出一个方案。

\(m\le 10^9,n\le 2\times 10^5\)

题解

下面的数组都从 \(0\) 开始标号。先将 \(a,b\) 数组分别从小到大排序,然后把 \(b\) 数组变成 \(b_0,b_1,b_2,\cdots,b_{n-1},b_0+m,b_1+m,\cdots,b_{n-1}+m,b_0+2m,b_1+2m,\cdots,b_{n-1}+2m\),把 \(a\) 数组变成 \(a_0+m,a_1+m,a_2+m,\cdots,a_{n-1}+m\)

考虑贪心,显然两个人的路径不能“交叉”,所以一定是小的和小的匹配,大的和大的匹配。问题变成了求 \(\min\limits_{0\le x\le 2n} \sum\limits_{i=0}^{n-1} |a_i-b_{i+x}|\) 的值。

\(ans_x=\sum\limits_{i=0}^{n-1} |a_i-b_{i+x}|\),把绝对值拆开,则变成了

\[\begin{aligned}ans_x&=\sum\limits_{i=0}^{n-1} |a_i-b_{i+x}| \\ &=\sum\limits_{i=0}^{n-1} ([a_i > b_{i+x}]a_i-[a_i < b_{i+x}]a_i-[a_i > b_{i+x}]b_{i+x}+[a_i < b_{i+x}]b_{i+x})\end{aligned}\]

\(sa_{x}=\sum\limits_{i=0}^{n-1} ([a_i > b_{i+x}]a_i-[a_i < b_{i+x}]a_i),sb_{x}=\sum\limits_{i=0}^{n-1} ([a_i < b_{i+x}]b_{i+x}-[a_i > b_{i+x}]b_{i+x})\)。考虑单独计算。

我们将 \(sa\) 差分,发现 \(sa_x-sa_{x-1}=\sum\limits_{i=0}^{n-1} ([a_i > b_{i+x}]a_i-[a_i < b_{i+x}]a_i-[a_i > b_{i+x-1}]a_i+[a_i < b_{i+x-1}]a_i)\),发现若 \(a_i \le b_{i+x-1} \le b_{i+x}\)\(b_{i+x-1} \le b_{i+x} \le a_i\) 时,这个式子恰好为 \(0\),只有当 \(b_{i+x-1} < a_i < b_{i+x}\) 时,这个式子的值为 \(-2a_i\),而这个 \(x\) 又是唯一的。于是对于每个 \(a_i\) 求出这个唯一的 \(x\),在差分数组上直接修改即可。

我们依次考虑每个 \(b_i\),发现当 \(x\) 小于某个值时,\(b_i\)\(sb_x\) 的贡献是负的,否则是正的。于是我们求出这个值,然后区间加贡献即可。这个也可以用差分简单的实现。

发现求 \(sa_x,sb_x\) 在实现的过程中都需要差分,不需要分开计算。

时间复杂度 \(O(n\log n)\)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
#include <vector>
int read(){
register int x = 0;
register char ch = getchar(), f = 1;
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = !f;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0');
return f ? x : -x;
}
#define N 200005
int n, ansi, ansid[N];
long long m, ans[N << 1], Ans;
struct node{
long long v;
int id;
bool operator < (const node &rhs) const {
return v < rhs.v;
}
}a[N], b[N * 3];
long long abs(long long a){
return a > 0 ? a : -a;
}
void add(int l, int r, long long v){
ans[l] += v, ans[r] -= v;
}
int main(){
m = read(), n = read();
for (register int i = 0; i < n; ++i) a[i].v = read(), a[i].id = i;
for (register int i = 0; i < n; ++i) b[i].v = read(), b[i].id = i;
std :: sort(a, a + n), std :: sort(b, b + n);
for (register int i = 0; i < n; ++i) a[i].v += m;
for (register int i = 0; i < (n << 1); ++i) b[i + n] = b[i], b[i + n].v += m;
for (register int i = 0; i < n; ++i) ans[0] += a[i].v;
for (register int i = 0; i < n; ++i){
int j = std :: lower_bound(b, b + 3 * n, a[i]) - b;
if (j > i && j <= i + (n << 1)) ans[j - i] -= 2 * a[i].v;
}
for (register int j = 0; j < 3 * n; ++j){
if (j < n){ add(0, j + 1, -b[j].v); continue; }
if (j >= (n << 1)){ add(j - n + 1, (n << 1) + 1, b[j].v); continue; }
int i = std :: upper_bound(a, a + n, b[j]) - a;
add(j - i + 1, j + 1, b[j].v), add(j - n + 1, j - i + 1, -b[j].v);
}
Ans = ans[0], ansi = 0;
for (register int i = 1; i <= (n << 1); ++i){
ans[i] += ans[i - 1];
if (ans[i] < Ans) Ans = ans[i], ansi = i;
}
for (register int i = 0; i < n; ++i)
ansid[a[i].id] = b[(i + ansi) % n].id;
printf("%lld\n", Ans);
for (register int i = 0; i < n; ++i) printf("%d ", ansid[i] + 1);
}

评论