[APC001G] Colorful Doors 题解

Description

有一座桥,上面有 \(2N\) 个传送门。这 \(2N\) 个传送门分成 \(N\) 组,每组恰好两个。我们用 \(1\)\(N\) 的数字来区分每组传送门。

你从最左边进入这座桥,沿着朝右的方向一直走,每次碰到一个传送门,你会传送到它对应的传送门的位置,然后继续往右,直到走到第 \(2N\) 个传送门右边为止。可以证明在这个问题中,我们一定能走到第 \(2N\) 个传送门右边。

比如 6 个门,依次为 \(1, 3, 2, 1, 2, 3\),用 \(1\)\(6\) 表示这些门的标号。那么你行走的路径为 \(1 \hookrightarrow 4 \rightarrow 5 \hookrightarrow 3 \rightarrow 4 \hookrightarrow 1 \rightarrow 2 \hookrightarrow 6\)。其中 \(\hookrightarrow\) 表示传送,\(\rightarrow\) 表示行走。

现在你忘了传送门的具体对应关系,只记得对于每一段 \(i \to i+1\) 你是否经过。问能不能是否存在一种合法的传送门的方案,如果存在的话,构造一组解。

\(N\leq 10^5\)

Solution

首先设 \(a_i\) 表示 \((i-1)\to i\) 是否经过,那么先把 \(a_1\)\(a_{2n+1}\) 设为 \(1\),即可以把走的过程看成一个环。

对于每个传送门,如果其左右的路都是 \(1\),则将其看成 M,只有左边是 \(1\) 看成 S,只有右边是 \(1\) 看成 T。

那么没有标记的传送门一定不会经过,这些门可以随便填。其余的一定会经过。

注意到 M 门需要向右经过一次和传送一次,S 门只能向右,T 只能传送经过,所以 M 只能和 M 自己匹配,而 S 只能和 T 匹配。

所以如果 M 的个数为偶数,则一定无解。


考虑没有 S 和 T 的情况。

如果 M 的个数是 \(4\) 的倍数,可以如下构造:\([1,2,1,2,3,4,3,4,\ldots,2n-1,2n,2n-1,2n]\)

如果模 \(4\)\(2\) 则一定无解。


现在考虑上 S 和 T。

首先如果 M 的个数是 \(4\) 的倍数,按照上面的方式把所有 M 的位置拿出来匹配,对于相邻的 ST 匹配在一起,经过手玩仍然是对的。具体可以看这个图(从这里拿的,侵删)

如果 \(M\) 的个数模 \(4\)\(2\),上面就不能直接做了。但是注意到上面的做法很强,在中间随便加入一些东西仍然是对的,所以考虑选择两个 M 进行匹配,并达到等效删除的作用。

考虑下面这种情况:

将相邻的 M 极长段拿出来,如果前面的段被两个 ST 包围,则可以分别将两段第一个的 M 匹配,同时第一个 ST 的 T 和第二个 ST 的 S 也进行匹配。

其余的不变,手玩可发现这么做对于其余的结构相当于是在其中嵌入一部分数。要想这东西大概是注意到前面将相邻 ST 匹配,如果不匹配一个 TS 就一定不会走出回头路把这两个数删掉。现在先作出一个 TS,然后显然要跨段,可以猜到是将两段分别第一个 M 进行匹配。

还有一种情况是这个:

和上面对称,容易证明如果找不到这样的两个段就一定不合法,因为这样的状态就一定长成 MMMSTMMM 的形式,还是通过手玩可以知道其不合法。

对于剩下没有匹配的位置,直接把这些位置拿出来做 M 模 \(4\)\(0\) 的做法即可。

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

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 2e5 + 5;

int n, m, cnt;
int a[kMaxN], op[kMaxN], id[kMaxN], res[kMaxN];
std::string s;

void add(int x, int y) {
  assert(!res[x] && !res[y]);
  res[x] = res[y] = ++cnt;
}

void dickdreamer() {
  std::cin >> n >> s; m = cnt = 0;
  a[1] = a[2 * n + 1] = 1;
  for (int i = 1; i <= 2 * n; ++i) res[i] = 0;
  for (int i = 2; i <= 2 * n; ++i) a[i] = s[i - 2] - '0';
  int cntM = 0;
  op[0] = op[2 * n + 1] = 0;
  for (int i = 1; i <= 2 * n; ++i) {
    if (!a[i] && a[i + 1]) op[i] = 1; // T
    else if (a[i] && !a[i + 1]) op[i] = 2; // S
    else if (a[i] && a[i + 1]) op[i] = 3, ++cntM; // M
    else op[i] = 0;
    // std::cerr << op[i] << ' ';
  }
  // std::cerr << '\n';
  if (cntM & 1) return void(std::cout << "No\n");
  if (cntM % 4 == 2) {
    int lstl = 0, lstr = 0;
    bool fl = 0;
    for (int l = 1, r = 1; l <= 2 * n; l = r + 1) {
      for (; l <= 2 * n && op[l] != 3; ++l) {}
      if (l > 2 * n) break;
      r = l;
      for (; r < 2 * n && op[r + 1] == 3; ++r) {}
      // std::cerr << "??? " << l << ' ' << r << '\n';
      if (lstl && lstr) {
        if (op[l - 1] == 1 && op[r + 1] == 2) {
          add(lstr, r), add(l - 1, r + 1), fl = 1;
        } else if (op[lstl - 1] == 1 && op[lstr + 1] == 2) {
          add(lstl, l), add(lstl - 1, lstr + 1), fl = 1;
        }
      }
      lstl = l, lstr = r;
      if (fl) break;
    }
    if (!fl) return void(std::cout << "No\n");
  }
  for (int i = 1; i <= 2 * n; ++i)
    if (!res[i] && op[i] == 3)
      id[++m] = i;
  assert(m % 4 == 0);
  for (int i = 1; i <= m; i += 4)
    add(id[i], id[i + 2]), add(id[i + 1], id[i + 3]);
  int lst[3] = {0};
  for (int i = 1; i <= 2 * n; ++i) {
    if (!res[i]) {
      // std::cerr << op[i] << '\n';
      if (op[i] == 0) {
        if (lst[0]) add(lst[0], i), lst[0] = 0;
        else lst[0] = i;
      } else if (op[i] == 1) {
        if (lst[2]) add(lst[2], i), lst[2] = 0;
        else lst[1] = i;
      } else {
        assert(op[i] == 2);
        if (lst[1]) add(lst[1], i), lst[1] = 0;
        else lst[2] = i;
      }
    }
  }
  std::cout << "Yes\n";
  for (int i = 1; i <= 2 * n; ++i) std::cout << res[i] << " \n"[i == 2 * 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 cid, T = 1;
  std::cin >> T >> cid;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}
posted @ 2025-06-10 21:56  下蛋爷  阅读(9)  评论(0)    收藏  举报