Codeforces 1710D Recover the Tree - 构造

题目传送门

  传送门

  因为一些奇奇怪怪的原因被迫去打 acm,sad....

  不难注意到一条很简单的性质:如果两个连通区间交非空,那么它们的交和并都是连通的。

  考虑把 1 当做根,根据这条性质可以知道剩下极大的连通区间两两无交且并集为 [2,n]

  注意到每个极大连续区间可以当做一个子问题,先假设我们递归得到了满足题目条件的这样一些连通块。

  现在考虑把它们连接起来,使得满足根的限制,以及它们之间的限制(不存在连通区间)。

  对于一个极大连通区间 [l,r]

  如果存在一个连通区间 [1,r] 满足 lrr,设 r0 是满足这个条件最小的 r,那么仅需把 r0 接到 1 上即可满足所有连通块之间的限制。根据最开始那条性质,也容易知道满足根给它的限制。

  如果不存在的话,如果 [1,l1] 不是连通区间,那么直接把这个连通块接在 1 上就可以了,否则把它接在 r1 上,这个 r1 是最小的满足 r>r[1,r] 为连通区间的 r

  注意到最后这一种情况可能对满足连通块之间的限制不是那么地显然。但是注意到一个性质,就是 (r,r1) 之间一定还包含一个极大连续区间,否则话不可能有满足条件的树。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#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;
}

  

posted @   阿波罗2003  阅读(100)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2017-07-27 bzoj 3309 DZY Loves Math - 莫比乌斯反演 - 线性筛
点击右上角即可分享
微信分享提示