@codeforces - [email protected] Employment

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

有 m 个城市围成一个圆环,编号为 1~m。
某公司有 n 个职员住在 m 个城市,依次住在编号为 a1, a2, ..., an 的城市(可能住在同一城市)。且该公司有 n 个工作场所,依次在 b1, b2, ..., bn(一样可以在同一城市)。
现在要将职员与城市一一匹配,使得每个职员的居所与工作场所距离之和最小,并输出方案。

Input
第一行两个整数 m, n(1 <= m <= 10^9, 1 <= n <= 200000),表示圆环上的城市个数以及职员个数。
第二行 n 个整数 a1, a2, ..., an(1 <= ai <= m),表示职员居住的城市编号
第三行 n 个整数 b1, b2, ..., bn(1 <= bi <= m),表示工作场所所在的城市编号。

Output
第一行输出最小距离总和。
第二行输出匹配方案,第 i 个数表示第 i 个职员工作的场所编号。

Examples
Input
10 3
1 5 5
10 4 6
Output
3
1 2 3

Input
10 3
1 4 8
8 3 6
Output
4
2 3 1

Note
第一个样例中,1 - 10, 5 - 4, 5 - 6 的距离都为 1。
第二个样例中,1 - 3, 4 - 6, 8 - 8 的距离分别为 2, 2, 0。

@[email protected]

巧妙地见过类似的原题。。。
然而我在考场上还是没有做出来,真是丢人。

先将 a, b 排好序。
首先将 bi 扩张成三倍,分别为 bi - m, bi, bi + m。则 ai 与 bj 之间在圆环上的距离肯定为 ai 到 bj - m, bj, bj + m 之中的某一个的数轴上的距离。
记这个扩张过后的 b 为 b‘。

假如一个方案中 ai 匹配 bx,aj 匹配 by,且 ai < aj, bx > by(即匹配出现交叉),则交换匹配变成 ai 匹配 by,aj 匹配 bx 会更优(分类讨论众多情况即可验证)。
同时还可以在原先的圆环上验证一个结论:如果 a1 匹配 b[x],则 a2 应依次匹配 b[x+1],a3 匹配 b[x+2],...。

贪心地得到了这两个结论后,我们最终有效的方案只有 2*n + 1 种。只要能有效地维护出每种方案的距离总和就可以解决这道题。
定义 ans[i] 表示 a1 匹配 i 的距离总和。

两个点 i, j 的距离定义为 |i - j|,其中 i >= j 时为 i - j,否则为 j - i。
一开始 a1 匹配 b1,匹配的点对之间距离总为 ai - bi。将 a1 对应的匹配往后移动时,ai - bj 就将变为 bj - ai。
于是对于每一个 ai,对一开始某个连续的区间的贡献为 +ai,对后面某个连续的区间的贡献为 -ai;bi 对一开始某个连续的区间的贡献 -bi,对后面某个连续的区间的贡献为 +bi。
找出那个变化的点(即 b[j] <= a[i] < b[j+1]),做一个差分即可。

@accepted [email protected]

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 200000;
const ll INF = (1LL<<60);
struct node{
    int pos; ll key;
    friend bool operator < (node a, node b) {
        return a.key < b.key;
    }
}a[MAXN + 5], b[3*MAXN + 5];
ll sum[3*MAXN + 5];
int res[MAXN + 5];
int n; ll m;
int main() {
    scanf("%lld%d", &m, &n);
    for(int i=1;i<=n;i++)
        scanf("%d", &a[i].key), a[i].pos = i;
    sort(a + 1, a + n + 1);
    a[n + 1].key = INF;
    for(int i=1;i<=n;i++)
        scanf("%d", &b[i].key), b[i].pos = i;
    sort(b + 1, b + n + 1);
    for(int i=1;i<=n;i++) {
        b[2*n + i] = b[n + i] = b[i];
        b[i].key -= m, b[2*n + i].key += m;
    }
    int p = 1;
    for(int i=1;i<=n;i++) {
        while( b[p].key <= a[i].key ) p++;
        sum[0] += a[i].key;
        if( p - i + 1 <= 2*n + 1 )
            sum[p - i + 1] -= 2*a[i].key;
    }
    p = 1;
    for(int i=1;i<=3*n;i++) {
        while( a[p].key < b[i].key ) p++;
        sum[max(0, i - n + 1)] -= b[i].key;
        if( i - p + 2 <= 2*n + 1 )
            sum[i - p + 2] += 2*b[i].key;
        sum[min(i + 1, 2*n + 2)] -= b[i].key;
    }
    int ans = 1;
    for(int i=1;i<=2*n+1;i++) {
        sum[i] += sum[i-1];
        if( sum[i] < sum[ans] )
            ans = i;
    }
    for(int i=1;i<=n;i++)
        res[a[i].pos] = b[ans+i-1].pos;
    printf("%lld\n", sum[ans]);
    for(int i=1;i<=n;i++)
        printf("%d ", res[i]);
}

@[email protected]

当时在网上随便找了一个题解,改改改写写写结果怎样都过不了。

后来自己一写就过了 =_=。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11553029.html

时间: 2024-10-09 13:22:36

@codeforces - [email protected] Employment的相关文章

@codeforces - [email&#160;protected] Mashmokh&#39;s Designed Problem

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,每个点的儿子是有序的. 现给定 m 次操作,每次操作是下列三种中的一种: (1)给定 u, v,询问 u, v 之间的距离. (2)给定 v, h,断开 v 到父亲的边,将 v 这棵子树加入到它的第 h 个祖先的最后一个儿子. (3)给定 k,询问在当前这棵树上

@codeforces - [email&#160;protected] T-Shirts

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 件 T-shirt,第 i 件 T-shirt 有一个 ci 和 qi,分别表示费用与质量. 同时有 k 个顾客,第 j 个顾客准备了 bj 的金钱去购买 T-shirt. 每个顾客的购买策略是相同的: 他会买他的资金范围内 q 值最大的一件,如果有多个选 c 最小的一件,每种

@codeforces - [email&#160;protected] Oleg and chess

目录 @description - [email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @description - [email protected] 给定一个 n*n 的棋

@codeforces - [email&#160;protected] Lucky Tickets

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 已知一个数(允许前导零)有 n 位(n 为偶数),并知道组成这个数的数字集合(并不一定要把集合内的数用完).求有多少种可能,使得这个数前半部分的数位和等于后半部分的数位和. 模 998244353. input 第一行两个整数:n k.表示这个数的位数以及组成这个数的数字集合大小.2

@codeforces - [email&#160;protected] Bandit Blues

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 求有多少个长度为 n 的排列,从左往右遍历有 a 个数比之前遍历的所有数都大,从右往左遍历有 b 个数比之前遍历的所有数都大. 模 998244323. input 一行三个整数 n

@codeforces - [email&#160;protected] Vus the Cossack and a Field

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的 01 矩阵,通过这个矩阵生成一个无穷矩阵,具体操作如下: (1)将这个矩阵写在左上角. (2)将这个矩阵每位取反写在右上角. (3)将这个矩阵每位取反写在左下角. (4)将这个矩阵写在右下角. (5)将得到的矩阵再作为初始矩阵,重复这些操作. 比如对于初始矩阵:

@codeforces - [email&#160;protected] Big Problems for Organizers

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个点连成一棵树,经过每条边需要花费 1 个单位时间. 现给出 m 次询问,每次询问给出两个点,需要求所有点同时出发,最终所有点到达这两个点之一的最小花费时间. input 第一行包含一个整数 n (2?≤?n?≤?100000) ,表示点数. 接下来 n-1 行每行两个 1~n 的

@codeforces - [email&#160;protected] Strongly Connected Tournament

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个选手参加了一场竞赛,这场竞赛的规则如下: 1.一开始,所有选手两两之间独立进行比赛(没有平局). 2.主办方将胜者向败者连边形成 n 个点的竞赛图. 3.主办方对这个竞赛图进行强连通分量缩点. 4.每一个强连通分量内部的选手重复步骤 1~3,直到每一个强连通分量内只剩一个选手.

@codeforces - [email&#160;protected] Rotate Columns (hard version)

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的矩阵 A. 定义一次操作为将矩阵的某一列竖着循环移位,你可以对任意列做任意次操作. 定义 ri 为第 i 行的最大值,最大化 r1 + r2 + ... + rn. Input 第一行一个整数 t (1≤t≤40),表示数据组数. 每组数据第一行包含两个整数 n m