1 条题解

  • 0
    @ 2025-8-24 22:54:10

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Alex_Wei
    **

    搬运于2025-08-24 22:54:10,当前版本为作者最后更新于2024-01-16 23:34:17,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    *P10046 [CCPC 2023 北京市赛] 哈密顿

    问题等价于每个 aia_i 匹配一个 bjb_j,不同的 aia_i 匹配不同的 bjb_j,且匹配关系形成一个大环。即若 aia_i 匹配 bjb_j 则连边 iji\to j,图是一个环。

    绝对值太烦人了,先去掉,则答案为 aibj\sum a_i - \sum b_j

    考虑到每个 ai<bja_i < b_j 会让答案增加 2(bjai)2(b_j - a_i),则问题等价于选择相同数量的 aia_ibjb_j,让选中的 aia_i 只会匹配选中的 bjb_j,匹配的 aia_ibjb_j 满足 ai<bja_i < b_j,并且除非全选,否则匹配关系不能成环。如果目前的匹配关系不成环,那么容易构造出补充匹配使得图是一个大环的情况。

    • 如果选出的匹配(钦定 ai<bja_i < b_j)存在 ai>bja_i > b_j 的情况,其贡献为 bjaib_j - a_i,只会将答案算小。
    • 如果补充的匹配(钦定 ai>bja_i > b_j)存在 ai<bja_i < b_j 的情况,其贡献为 aibja_i - b_j,只会将答案算小。
    • 要让选出的匹配不成环,只需让选出的 aia_i 下标集合 AAbjb_j 下标集合 BB 不完全相同。每次选 A\BA\backslash B 的某个 ii,让它匹配 ABA\cap B 的某个 jj,然后从 AA 删去 ii,从 BB 删去 jj,则 A\BA\backslash B 大小不变且一定不会成环。假设 uvu\to v 成环,那么 vBv\in Bvv 之前作为某个 ii 出现,即当时 vAv\in A,则当时 vABv\in A\cap B,和 vA\Bv\in A\backslash B 矛盾。当 AB=A\cap B = \varnothing 时随便匹配就行。因为初始 A\BA\backslash B \neq \varnothing,所以构造可行。

    因此,我们的目标是在选中的两组下标集合不完全相同的限制下,最大化选出的 bjai\sum b_j - \sum a_i

    aia_ibjb_j 从小到大排序,设它们原来的下标为 cic_idjd_j。显然,如果没有限制,则一定会选 a1ka_{1\sim k}bnk+1nb_{n - k + 1\sim n},满足 ak<bnk+1a_k < b_{n - k + 1}ak+1>bnka_{k + 1} > b_{n - k}。先这么选着,如果不符合条件,说明 A=BA = B0<A<n0 < |A| < n,需要对方案进行调整。

    ΔA\Delta |A| 分类讨论:

    • ΔA=1\Delta |A| = -1,则
      • ckdnk+1c_k \neq d_{n - k + 1} 或者 A=1|A| = 1,则将 ckc_kdnk+1d_{n - k + 1} 分别从 AABB 中删去。
      • 否则删去 ckc_kdnk+2d_{n - k + 2},或 ck1c_{k - 1}dnk+1d_{n - k + 1}
    • ΔA=0\Delta |A| = 0,则将 ckc_kdnk+1d_{n - k + 1} 删去,换成 ckc_kdnkd_{n - k},或 ck+1c_{k + 1}dnk+1d_{n - k + 1}
    • ΔA=1\Delta |A| = 1,则
      • ck+1dnkc_{k + 1}\neq d_{n - k}A=n1|A| = n - 1,则加入 ck+1c_{k + 1}dnkd_{n - k}
      • 否则加入 ck+1c_{k + 1}dnk1d_{n - k - 1},或 ck+2c_{k + 2}dnkd_{n - k}

    正确性证明分两步,一步是证明给出的方案是在固定 ΔA\Delta |A| 之后最优的,另一步是证明其它 ΔA\Delta |A| 一定不优于 ΔA=±1\Delta |A| = \pm 1 的情况。具体细节留给读者自行补充。

    时间复杂度为除排序外线性。

    #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
    上传者