CF578E Walking! 題解

下蛋爷發表於2024-07-26

Description

  • 給定一個長度為 \(n\) 的只包含 L,R 的字串 \(s\)
  • 構造一個 \(n\) 排列 \(p\) 滿足 \(s[p_i] \ne s[p_{i+1}](1 \le i < n)\)
  • 最小化 \(p\)\(p_i > p_{i+1}(1 \le i < n)\) 的數量。
  • \(n \le 10^5\),資料保證有解。

Solution

考慮把 \(p\) 中的每個極長連續上升子序列拿出來,顯然這些子序列為 \(1,2,\ldots,n\) 的一個劃分,使得每個子序列一定是 LR 交錯出現。

所以第一問的答案為把 \(1,2,\ldots,n\) 劃分成最少的子序列數\(-1\),使得每個子序列是 LR 交錯出現。

有一個顯然的貪心策略是從前往後掃,不妨設 \(s_i\) 為 L,如果 \(s_i\) 能找到末尾與其不同的子序列就把它加到那個子序列末尾,如果找不到就建立一個新的子序列。

證明就考慮如果能找到但是不加,就說明當前答案會 \(+1\),如果加了頂多是後面的一個 R 找不到 L 與其配對,也只會 \(+1\),並且在這之前答案一直更優。

然後需要構造方案,用 \(cnt_{ij}\) 表示開頭為 \(i\),末尾為 \(j\) 的子序列數量。

容易發現當 \(cnt_{LR},cnt_{RL}\neq 0,cnt_{LL}=cnt_{RR}=0\) 時是無法直接構造方案的,這時可以任意取一對 LR 和 RL 的序列,把末尾更靠後的那個序列的末尾放到另一個序列的末尾,這樣就有了 LL 和 RR。

注意到 \(|cnt_{LL}-cnt_{RR}|\leq 1\),不妨設 \(cnt_{LL}\geq cnt_{RR}\),可以構造:LR、LR、...、LR、LL、RL、RL、...、RL、RR、LL、RR、LL、...

時間複雜度:\(O(n)\)

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e5 + 5;

int n, tot;
std::string str;
std::vector<int> vec[2], vv[kMaxN], id[2][2];

void fix() {
  int x = id[0][1].back(), y = id[1][0].back();
  id[0][1].pop_back(), id[1][0].pop_back();
  if (vv[x].back() < vv[y].back()) std::swap(x, y);
  vv[y].emplace_back(vv[x].back()), vv[x].pop_back();
  id[str[vv[x].front()] == 'R'][str[vv[x].back()] == 'R'].emplace_back(x);
  id[str[vv[y].front()] == 'R'][str[vv[y].back()] == 'R'].emplace_back(y);
}

void print(int x) {
  for (auto i : vv[x]) std::cout << i << ' ';
}

void dickdreamer() {
  std::cin >> str;
  n = str.size(); str = " " + str;
  for (int i = 1; i <= n; ++i) {
    int c = (str[i] == 'R');
    if (!vec[c ^ 1].size()) {
      vec[c].emplace_back(++tot);
      vv[tot] = {i};
    } else {
      int t = vec[c ^ 1].back(); vec[c ^ 1].pop_back();
      vv[t].emplace_back(i), vec[c].emplace_back(t);
    }
  }
  std::cout << tot - 1 << '\n';
  for (int i = 1; i <= tot; ++i)
    id[str[vv[i].front()] == 'R'][str[vv[i].back()] == 'R'].emplace_back(i);
  if (id[0][1].size() && id[1][0].size() && !id[0][0].size() && !id[1][1].size()) fix();
  int o = 0;
  if (id[0][0].size() < id[1][1].size()) o = 1;
  for (auto x : id[o][o ^ 1]) print(x);
  if (id[o][o].size()) print(id[o][o].front());
  for (auto x : id[o ^ 1][o]) print(x);
  for (int i = 0; i < (int)id[o ^ 1][o ^ 1].size(); ++i) {
    print(id[o ^ 1][o ^ 1][i]);
    if (i + 1 < (int)id[o][o].size()) print(id[o][o][i + 1]);
  }
}

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;
}