1 条题解
-
0
自动搬运
来自洛谷,原作者为

Alex_Wei
**搬运于
2025-08-24 22:54:10,当前版本为作者最后更新于2024-01-16 23:34:17,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
*P10046 [CCPC 2023 北京市赛] 哈密顿
问题等价于每个 匹配一个 ,不同的 匹配不同的 ,且匹配关系形成一个大环。即若 匹配 则连边 ,图是一个环。
绝对值太烦人了,先去掉,则答案为 。
考虑到每个 会让答案增加 ,则问题等价于选择相同数量的 和 ,让选中的 只会匹配选中的 ,匹配的 和 满足 ,并且除非全选,否则匹配关系不能成环。如果目前的匹配关系不成环,那么容易构造出补充匹配使得图是一个大环的情况。
- 如果选出的匹配(钦定 )存在 的情况,其贡献为 ,只会将答案算小。
- 如果补充的匹配(钦定 )存在 的情况,其贡献为 ,只会将答案算小。
- 要让选出的匹配不成环,只需让选出的 下标集合 和 下标集合 不完全相同。每次选 的某个 ,让它匹配 的某个 ,然后从 删去 ,从 删去 ,则 大小不变且一定不会成环。假设 成环,那么 且 之前作为某个 出现,即当时 ,则当时 ,和 矛盾。当 时随便匹配就行。因为初始 ,所以构造可行。
因此,我们的目标是在选中的两组下标集合不完全相同的限制下,最大化选出的 。
将 和 从小到大排序,设它们原来的下标为 和 。显然,如果没有限制,则一定会选 和 ,满足 但 。先这么选着,如果不符合条件,说明 且 ,需要对方案进行调整。
对 分类讨论:
- 若 ,则
- 若 或者 ,则将 和 分别从 和 中删去。
- 否则删去 和 ,或 和 。
- 若 ,则将 和 删去,换成 和 ,或 和 。
- 若 ,则
- 若 或 ,则加入 和 。
- 否则加入 和 ,或 和 。
正确性证明分两步,一步是证明给出的方案是在固定 之后最优的,另一步是证明其它 一定不优于 的情况。具体细节留给读者自行补充。
时间复杂度为除排序外线性。
#include <bits/stdc++.h> using namespace std; using ll = long long; using pii = pair<int, int>; using pll = pair<ll, ll>; using pdi = pair<double, int>; using pdd = pair<double, double>; using ull = unsigned long long; using LL = __int128_t; #define ppc(x) __builtin_popcount(x) #define clz(x) __builtin_clz(x) bool Mbe; // ---------- templates above ---------- constexpr int N = 1e5 + 5; int n; struct dat { int val, id; bool operator < (const dat &z) const { return val < z.val; } } a[N], b[N]; void solve() { cin >> n; for(int i = 1; i <= n; i++) { cin >> a[i].val >> b[i].val; a[i].id = b[i].id = i; } sort(a + 1, a + n + 1); sort(b + 1, b + n + 1); int l = 0, r = n + 1; map<int, int> mp; ll ans = 0; while(l < n) { if(a[l + 1].val > b[r - 1].val) break; mp[a[++l].id]++, mp[b[--r].id]--; ans += b[r].val - a[l].val; } bool ok = 0; for(pii it : mp) ok |= it.second > 0; if(!ok && l < n && l) { ll res = -1e18; auto f = [&](int l, int r) { return ll(b[r].val - a[l].val); }; if(a[l].id != b[r].id || l == 1) res = max(res, -f(l, r)); else res = max(res, max(-f(l, r + 1), -f(l - 1, r))); res = max(res, max(f(l, r - 1), f(l + 1, r)) - f(l, r)); if(a[l + 1].id != b[r + 1].id || l == n - 1) res = max(res, f(l + 1, r - 1)); else res = max(res, max(f(l + 2, r - 1), f(l + 1, r - 2))); ans += res; } ans *= 2; for(int i = 1; i <= n; i++) ans += a[i].val - b[i].val; cout << ans << endl; } bool Med; signed main() { fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0); // ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); #ifdef ALEX_WEI FILE* IN = freopen("1.in", "r", stdin); FILE* OUT = freopen("1.out", "w", stdout); #endif int T = 1; while(T--) solve(); fprintf(stderr, "%.3lf ms\n", 1e3 * clock() / CLOCKS_PER_SEC); return 0; } /* g++ a.cpp -o a -std=c++14 -O2 -DALEX_WEI */
- 1
信息
- ID
- 9663
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者