題目傳送門
因爲一些奇奇怪怪的原因被迫去打 acm,sad....
不難注意到一條很簡單的性質:如果兩個連通區間交非空,那麼它們的交和並都是連通的。
考慮把 $1$ 當做根,根據這條性質可以知道剩下極大的連通區間兩兩無交且並集爲 $[2, n]$。
注意到每個極大連續區間可以當做一個子問題,先假設我們遞歸得到了滿足題目條件的這樣一些連通塊。
現在考慮把它們連接起來,使得滿足根的限制,以及它們之間的限制(不存在連通區間)。
對於一個極大連通區間 $[l, r]$
如果存在一個連通區間 $[1, r']$ 滿足 $l \leqslant r' \leqslant r$,設 $r_0$ 是滿足這個條件最小的 $r'$,那麼僅需把 $r_0$ 接到 $1$ 上即可滿足所有連通塊之間的限制。根據最開始那條性質,也容易知道滿足根給它的限制。
如果不存在的話,如果 $[1, l - 1]$ 不是連通區間,那麼直接把這個連通塊接在 $1$ 上就可以了,否則把它接在 $r_1$ 上,這個 $r_1$ 是最小的滿足 $r' > r$ 且 $[1, r']$ 爲連通區間的 $r'$。
注意到最後這一種情況可能對滿足連通塊之間的限制不是那麼地顯然。但是注意到一個性質,就是 $(r, r_1)$ 之間一定還包含一個極大連續區間,否則話不可能有滿足條件的樹。
Code
#include <bits/stdc++.h> using namespace std; const int N = 2e3 + 5; int T; int n; char buf[N]; bool f[N][N]; void dividing(int l, int r) { bool flg = true; for (int l0 = l + 1, r0 = r; l0 <= r; l0 = r0 + 1, r0 = r) { while (!f[l0][r0]) r0--; dividing(l0, r0); for (int i = l0; i <= r; i++) { if (f[l][i]) { if (i > r0) { if (flg) { printf("%d %d\n", r0, i); flg = false; } else { printf("%d %d\n", l, l0); } } else { flg = true; printf("%d %d\n", l, i); } break; } } } } void solve() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%s", buf + i); for (int j = i; j <= n; j++) { f[i][j] = (buf[j] == '1'); } } dividing(1, n); } int main() { scanf("%d", &T); while (T--) { solve(); } return 0; }