https://codeforces.com/contest/1503

C. Travelling Salesman Problem

 

题意:有 nn2n1052\le n\le 10^5 座城市,有魅力值 aia_i,最低票价 cic_i,现在从 1 号城市出发,经过每个城市恰一次,最终回到 1 号城市,从 iijj 的代价等于 max(ci,ajai)\max(c_i,a_j-a_i)0ai,ci1090\le a_i,c_i\le 10^9。问最少总代价。

思维+贪心

首先要发现路径是个环,所以从哪个城市出发都是一样的,无非就是环从哪里断开。

要经过所有城市恰一次,那么 ci\sum c_i 是至少的。因此先把 ci\sum c_i 加到答案里,这样从 iijj 的代价就变成了 max(0,ajaici)\max(0,a_j-a_i-c_i)

再发现如果 aj<aia_j<a_i,那么从 iijj 的代价就必定等于 cic_i,如果所有路径都满足出发地的 aa 值更大,那么最终答案就是下限 ci\sum c_i

把所有城市按照 aia_i 排序,从右边向左边走是免费的(即代价为 cic_i,已经统一加进答案)。但是因为路径是环形的,所以必定还要有从 11nn 的一条路径,这就使得答案可能大于下限。

从左向右走代价为:若 aj>ai+cia_j>a_i+c_i,则为 aj(ai+ci)a_j-(a_i+c_i)。否则为 00

考虑如果直接从 11 走到 nn。若 an>a1+c1a_n>a_1+c_1,则答案为 ana1c1a_n-a_1-c_1;若 ana1+c1a_n\le a_1+c_1,则答案就等于下限,这种情况就不讨论了。

假设从 11 走到 uu,再走到 nn,且这两段都不免费,则答案为 anaucu+aua1c1a_n-a_u-c_u+a_u-a_1-c_1,即 ana1c1cua_n-a_1-c_1-c_u 那么显然这种方案比直接从 11 走到 nn 代价更低。

所以策略就是从 1 走到 n,中间尽量多走不免费的边,有点反直觉,但是可以理解为原来是走一条从 1 到 n 的大边,这条边代价太高,所以分解成尽量多条小的边。

走一条边的代价为 aj(ai+ci)a_j-(a_i+c_i),最终代价等于 aj(ai+ci)\sum a_j-(a_i+c_i),所以显然对于一个目标点 jj,要从前面所有点中 ai+cia_i+c_i 最大的点作为出发点走过来。

下面用 ii 代表当前路径上 a+ca+c 最大的点。

很巧的是,ii 一定是当前贪心选择的这条路径到目前为止的终点,也就是说不会发生它已经连到其它点的情况。证明:当 aj>ai+cia_j>a_i+c_i 时,必定走 (i,j)(i,j) 这条边,而 aj+cja_j+c_j 一定大于 ai+cia_i+c_i,也就是说目标点 jj 一定会变成新的 a+ca+c 最大的点。

还要注意到当 ajai+cia_j\le a_i+c_i 时也是可能会走到 jj 的,这时虽然对答案没有贡献,但是如果它的 aj+cj>ai+cia_j+c_j>a_i+c_i,那么它就成为了新的 a+ca+c 最大的点,因此以后也是需要以它作为出发点的,所以要加进路径中。要注意这不代表所有点都能加进路径里,因为其它对答案没有贡献的点如果作为出发点,就不能保证 a+ca+c 最大的点是当前路径的终点,也就不能保证能连到这个 a+ca+c 最大的点了。

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
#include <bits/stdc++.h>
#define debug(x) cout << #x << ":\t" << (x) << endl;
using namespace std;
#define ll long long
#define ull unsigned ll
const int N = 2e6 + 10;
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const ll inv2 = (mod + 1) / 2;
int n;
struct X
{
ll a, c;
bool operator<(const X& b)const {
return a < b.a;
}
}t[N];

int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld%lld", &t[i].a, &t[i].c);
}
sort(t + 1, t + n + 1);
ll ans = 0, mx = t[1].a + t[1].c;
for (int i = 1; i <= n; i++) {
ans += t[i].c;
if (t[i].a > mx) {
ans += t[i].a - mx;
}
mx = max(mx, t[i].a + t[i].c);
}
printf("%lld\n", ans);
return 0;
}