CF1710D Recover the Tree

发布时间 2023-09-24 19:31:07作者: 徐子洋

题目链接

一个比较显然的思路就是:我们按照右端点从小到大的顺序(右端点相同按左端点从大到小)去考虑每个好的区间。

由于是连通性问题,不难想到用并查集去实时维护连通性。

根据定义,一个好的区间必定对应了一个连通块;我们考虑的是好的区间,所以当前并查集中的每个连通块必定都是一个区间。而在加入某个点前,这个连通块可能是好几个更小的连通块。换句话说,是区间中的某个点,串联起了其它连通块。

有了上述思维方向的转化,正解也就比较自然了。一个区间由好几个子区间组成,假设它们为 \([l_1,r_1],[l_2,r_2],\dots,[l_m,r_m]\)。不妨采取分类讨论进行构造(假设当前的区间为 \([l,r]\)):

  1. \(m=1\)

    当前区间已经连通,不作处理。

  2. \(m=2\)

    连接 \((l,r)\)

  3. \(m=3\)

    此时无解。必须得放到更大的区间里去处理才会有解。

  4. \(m>3\)

    连接 \((l,r),(l,r_{m-1})\),以及 \((r_{2},r),(r_{3},r),\dots,(r_{m-2},r)\)

    注意区间拆分中的 \(l_1\) 就是 \(l\)\(r_m\) 即为 \(r\)

点击查看代码
#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); ++i)
#define FR(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
const int N = 2010;
int n, t, fa[N]; char e[N][N];
void solve(){
    scanf("%d", &n);
    FL(i, 1, n) scanf("%s", e[i] + (fa[i] = i));
    FL(j, 1, n) FR(i, j - 1, 1){
        if(fa[j] <= i || e[i][j] == '0') continue;
        printf("%d %d\n", i, j);
        if((t = fa[fa[j] - 1]) > i){
            printf("%d %d\n", i, fa[j] - 1);
            for(int u = t - 1; fa[u] > i; u = fa[u] - 1)
                printf("%d %d\n", fa[u], j);
        }
        FR(k, j, i){
            if(fa[k] == i) break;
            fa[k] = fa[i];
        }
    }
}
int main(){
    int T; scanf("%d", &T);
    while(T--) solve();
    return 0;
}