CF2165D Path Split 题解

Description

给定一个长度为 \(n\) 的整数序列 \(a_1, a_2, \ldots, a_n\)。

你希望把 \(a\) 划分成若干个子序列 \(b_1, b_2, \ldots, b_k\),并满足以下条件:

  1. \(a\) 中的每个元素恰好属于某个 \(b_i\);
  2. 对于每个子序列 \(b_i\),设其元素为 \(b_{i,1}, b_{i,2}, \ldots, b_{i,p_i}\)。那么对所有 \(1 \le j < p_i\),都必须满足 \(|b_{i,j} - b_{i,j+1}| = 1\)

请计算,将 \(a\) 划分成满足条件的子序列所需的最小数量。

\(1\leq n\leq 10^6\)。

Solution

首先这题是个典型的最小链覆盖的问题,考虑转成二分图最大匹配的形式:对于每个 \(i\),建立左部点 \(L_i\) 和右部点 \(R_i\)。如果 \(i<j\) 且 \(|a_i-a_j|=1\),则让 \(L_i\) 和 \(R_j\) 连边。答案是 \(n-最大匹配\)。

直接跑匹配显然是不行的,考虑将其转化为贪心。

我们枚举左部点,考虑为每个左部点找到与其匹配的右部点。

由于左部点中值为 \(1\) 的只能匹配 \(2\),而 \(3\) 能匹配 \(2\) 和 \(4\),所以 \(3\) 强于 \(1\),那么一定是先为 \(1\) 找到匹配,再为 \(3\) 找到匹配。

而对于左部点的 \(2\) 和 \(4\) 情况会不太一样,因为 \(2\) 能匹配 \(1\) 和 \(3\),\(4\) 能匹配 \(3\) 和 \(5\),此时 \(2\) 不止能匹配 \(3\)。但是注意到如果存在一个 \(1\) 没有和 \(2\) 匹配,那么这个 \(1\) 就不能被匹配了。所以策略是对于每个 \(2\),如果能匹配 \(1\),就找到离它最近的 \(1\) 匹配,否则就找最近的 \(3\) 匹配。

最终策略就是从小到大枚举左部点的权值 \(v\),如果当前位置 \(x\) 能和值为 \(v-1\) 的右部点匹配,就找最近的 \(v-1\) 匹配,否则找最近的 \(v+1\) 匹配。

时间复杂度:\(O(n\log n)\)。

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e6 + 5;

int n;
int a[kMaxN];
std::vector<int> pos[kMaxN * 2];
std::set<int> st[kMaxN * 2];

void dickdreamer() {
  std::cin >> n;
  for (int i = 1; i <= 2 * n + 1; ++i) pos[i].clear(), st[i].clear();
  for (int i = 1; i <= n; ++i) std::cin >> a[i], pos[a[i]].emplace_back(i), st[a[i]].emplace(i);
  int ans = n;
  for (int i = 1; i <= 2 * n; ++i) {
    std::reverse(pos[i].begin(), pos[i].end());
    for (auto x : pos[i]) {
      auto it = st[i - 1].lower_bound(x);
      if (it != st[i - 1].end()) {
        --ans, st[i - 1].erase(it);
      } else {
        it = st[i + 1].lower_bound(x);
        if (it != st[i + 1].end()) --ans, st[i + 1].erase(it);
      }
    }
  }
  std::cout << ans << '\n';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}
posted @ 2025-11-17 20:09  下蛋爷  阅读(81)  评论(0)    收藏  举报