CF1178H Sol
update on 2024/1/2:更新了代码以及对 check 的方式的描述,主要问题就是只需要知道能换成的
5
97 3
30 3
80 29
82 6
10 14
35 26
33 5
29 49
45 12
34 60
也可以结合这组数据食用:
2
8 6
5 2
6 2
8 6
CF 官解的网络流建模是不是错的啊……我自己写的那种常数大一点,点数有
首先这种最小化问题,如果不好直接求就考虑判定。二分一个结束时间
那么现在再来判定就很简单了,是一个标准的最小费用最大流问题。交换一次费用为
- 首先,源点
S 向i\in[1,n] 连边,容量和费用分别为(1,0) ,表示初始情况下有一张邮票i 。 -
- 对于每个
i\in[1,n] ,向每个j 满足j\in[1,2n],b_i\ge b_j 连边,容量和费用分别为(+\infty,1) ,表示在0 时刻i 可以换成j 。 - 对于每个
i\in[1,2n] ,向每个j 满足j\in(n,2n],c_i\ge c_j 连边,容量和费用分别为(+\infty,1) ,表示在t 时刻i 可以换成j 。
那么最后如果该图的最大流为
发现如果我们把点按
其中边权表示的是费用,容量都是
对于
但是这仍然复杂度很高嘞,每次二分 check 都要跑一次,多了个
考虑对于每种
代码实现细节我觉得蛮多的……调了好久好久,多注意一下吧。
//Code by Shinobu Sengoku.
#include <bits/stdc++.h>
#define ALL(v) begin(v), end(v)
using i64 = int64_t;
using std::cin;
using std::cout;
constexpr int N = 1.11e5 + 5;
int n, n2, n3, tot = 1, ss, tt, maxflow;
std::array<int, N> a, b, x, y, cnt;
std::array<i64, N> c;
std::array<int, N * 3> to, head, nxt, limit, cost;
auto addedge(int u, int v, int f, int c) {
to[++tot] = v, limit[tot] = f, cost[tot] = c, nxt[tot] = head[u], head[u] = tot;
to[++tot] = u, limit[tot] = 0, cost[tot] = -c, nxt[tot] = head[v], head[v] = tot;
return ;
}
auto check(int t) {
std::fill(&cnt[1], &cnt[n2 + 1], 0);
std::iota(&x[1], &x[n2 + 1], 1);
for (auto i = 1; i <= n2; ++i)
c[i] = (i64)a[i] * t + (i64)b[i];
std::stable_sort(&x[1], &x[n2 + 1], [&](int p, int q) {
return (b[p] == b[q] ? c[p] > c[q] : b[p] < b[q]); });
auto cur = (i64)0;
for (auto i = 1; i <= n2; ++i) {
auto j = x[i];
c[j] = (i64)a[j] * t + (i64)b[j];
cur = std::max(cur, c[j]);
if (j <= n) c[j] = cur;
}
std::iota(&y[1], &y[n2 + 1], 1);
std::stable_sort(&y[1], &y[n2 + 1], [&](int p, int q) {
return (c[p] == c[q] ? p > q : c[p] < c[q]); });
cur = 0;
for (auto i = n2; i; --i) {
cur += (y[i] <= n ? 1 : -1);
if (cur < 0) return 0;
}
return 1;
}
namespace MCMF { // MinCost MaxFlow
std::array<bool, N> vis;
std::array<int, N> dis, flow, pre;
std::queue<int> q;
auto mincost(int s, int t) {
auto ret = (i64)0;
while (1) {
std::fill(&dis[1], &dis[t + 1], 2e9), std::fill(&vis[1], &vis[t + 1], 0);
while (!q.empty()) q.pop();
dis[s] = 0, flow[s] = N, q.emplace(s), vis[s] = 1;
while (!q.empty()) {
auto u = q.front(); q.pop();
vis[u] = 0;
for (auto i = head[u]; i; i = nxt[i]) {
auto v = to[i];
if (dis[v] > dis[u] + cost[i] && limit[i]) {
pre[v] = i, dis[v] = dis[u] + cost[i];
flow[v] = std::min(flow[u], limit[i]);
if (!vis[v]) q.emplace(v), vis[v] = 1;
}
}
}
if (dis[t] == 2e9) return ret;
ret += (i64)dis[t] * flow[t], maxflow += flow[t];
for (auto u = t; u != s; u = to[pre[u] ^ 1])
limit[pre[u]] -= flow[t], limit[pre[u] ^ 1] += flow[t];
}
}
} // namespace MCMF
auto build(int t) {
std::iota(&x[1], &x[n2 + 1], 1);
std::sort(&x[1], &x[n2 + 1], [&](int p, int q) {
return (b[p] == b[q] ? p > q : b[p] > b[q]); });
for (auto i = 1; i <= n2; ++i) {
addedge(i + n2, x[i], N, 1);
if (i < n2) addedge(i + n2, i + 1 + n2, N, 0);
}
for (auto i = 1; i <= n2; ++i)
c[i] = (i64)a[i] * t + (i64)b[i];
std::iota(&y[n + 1], &y[n2 + 1], n + 1);
std::sort(&y[n + 1], &y[n2 + 1], [&](int p, int q) {
return (c[p] == c[q] ? p > q : c[p] > c[q]); });
for (auto i = n + 1; i <= n2; ++i) {
addedge(i + n3, y[i], N, 1);
if (i < n2) addedge(i + n3, i + 1 + n3, N, 0);
}
ss = 5 * n + 1, tt = ss + 1;
for (auto i = 1; i <= n; ++i) addedge(ss, i, 1, 0);
for (auto i = n + 1; i <= n2; ++i) addedge(i, tt, 1, 0);
for (auto i = 1; i <= n; ++i) {
auto lt = 0, rt = n2 + 1;
while (lt + 1 < rt) {
auto mid = lt + rt >> 1;
if (b[i] >= b[x[mid]]) rt = mid;
else lt = mid;
}
if (rt >= 1 && rt <= n2) addedge(i, rt + n2, N, 0);
}
for (auto i = 1; i <= n2; ++i) {
auto lt = n, rt = n2 + 1;
while (lt + 1 < rt) {
auto mid = lt + rt >> 1;
if (c[i] >= c[y[mid]]) rt = mid;
else lt = mid;
}
if (rt > n && rt <= n2) addedge(i, rt + n3, N, 0);
}
return;
}
auto main() -> int {
std::ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n, n2 = n << 1, n3 = n * 3;
for (auto i = 1; i <= n2; ++i) cin >> a[i] >> b[i];
auto lt = -1, rt = (int)2e9 + 1;
while (lt + 1 < rt) {
auto mid = (lt - rt >> 1) + rt;
if (check(mid)) rt = mid;
else lt = mid;
}
if (rt == 2e9 + 1) cout << "-1\n";
else {
build(rt);
cout << rt << ' ' << MCMF::mincost(ss, tt) << "\n";
}
return 0;
}