• Codeforces Round #583 F Employment


    原题

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

    题目大意

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

    数据规模:(m le 10^9) (n le 2 imes 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{egin{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} ight. ]

    注意到这个表达式分为四段。分段点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
    ", 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]);
    }
    
    
  • 相关阅读:
    MIRO校验过程
    SAP中寄售处理
    物料BOM和生产订单BOM的区别
    sap 中怎样把非限制库存转为销售订单库存?
    SAP MM Consignment 寄售库存
    SAP 库存关联表信息
    SAP 物料主数据屏幕增强
    __defineGetter__ && __defineSetter__
    mongodb(分片)
    mongodb(副本集)
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/11502617.html
Copyright © 2020-2023  润新知