P4264 [USACO18FEB] Teleportation S 题解

题意

  • n 个物品,分别需要从 ai 运输到 bi 处,x=0 处有一个传送门入口,选择一个传送门出口 y,使得总运输距离 d 最小,输出最小值。

分析

  • 很明显,传送门对于总距离 d 的贡献是可以拆分成对于每一个物品的贡献之和的,因为有 d=di
  • 然后我们分析一下 y 的选址对于每一个物品的 di 的贡献。
  • 通过画图和分类讨论可以发现,一共有两种情况,而且每个 y 对应的 di=fi(y)=min(|aibi|,|ai|+|biy|) 是分段且线性的,根据线性函数的一些性质,我们可以利用对斜率的差分来求解 d=minyRfi(y)

画图

  1. |a|>|ab|
  • 这时候肯定不会走传送门的,因为运去 x=0 那里都比直接运要远。
  1. |a|<|b|
    0<a<b
  • 如图所示,这种情况下三个分段点分别是 2ab2b2a
  1. a<0<b/b<0<a

  • 如图所示,这种情况下的三个分段点是 0b2b
  • 而且,这三种情况的初始值均为 |ab|,所以初始值 f() 要设成 |aibi|

实现

  • 对于每一个物品,根据上图分类后利用 map 来存储差分的数组。对第一个分段位置 1,对第二个 +2,最后一个 1。处理完之后按 y 的值从小到大遍历 map,对于每一个 f(y) 的分段点的值求最小值即可。

代码

#include <bits/stdc++.h>
#define inf 0x7fffffff
#define int long long
using namespace std;
int n, x, y = -inf, s, ans;
map<int, int> mp;
inline void read(int &x) {
int w = 1;
char ch = x = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') w *= -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + ch - 48;
ch = getchar();
}
x *= w;
return ;
}
signed main() {
read(n);
int a, b;
for (int i = 1; i <= n; i++) {
read(a), read(b);
x += abs(a - b);
if (abs(a) > abs(a-b)) continue; //第一种情况
mp[b] += 2;
if ((a < b && a < 0) || (a >= b && a >= 0)) { //处理后两种情况
mp[0]--;
mp[b << 1]--;
} else if ((a < b && a >= 0) || (a >= b && a < 0)) {
mp[(b - a) << 1]--;
mp[a << 1]--;
}
}
ans = x;
int now, tmp;
for (auto it : mp) { //对于每一个分段点都统计,取最小值
now = it.first, tmp = it.second;
x += s * (now - y);
y = now;
s += tmp;
ans = min(ans, x);
}
printf("%lld", ans);
return 0;
}
posted @   HappyJaPhy  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示