@description@

对于一棵 n 个点的树,我们称两个点是相邻的当且仅当两个点的距离 <= 2。

现在给定 n 个集合,每一个集合表示树上某个点的相邻点是哪些。不过你不知道哪个集合对应哪个点。

现在这些集合构造出原树。保证至少存在一个解。如果多解,输出任意解即可。

原题传送门。

@solution@

两个点 x, y 对应的集合交集在树上仍然是一个连通块。

如果两个点 x, y 在树上距离 > 4,则它们集合交集为空。

如果两个点 x, y 在树上距离 = 4,则它们集合交集大小为 1。

如果两个点 x, y 在树上距离 = 3,则它们集合交集大小为 2。

如果两个点 x, y 在树上距离 = 2,则它们集合交集大小 >= 3。

如果两个点 x, y 在树上距离 = 1,则它们集合交集大小取决于 n:n = 2 时交集大小为 2;否则交集大小 >= 3。

集合交集为 2,该交集就对应着树上的一条边。

我们用 bitset 维护集合,并通过取交集找出所有的这种类型的边。

如果找不到一条这种类型的边,则只能是菊花图。特判即可。

否则,因为 n = 2 则已经找出了所有边,所以我们现在默认 n ≠ 2。

此时找出的边其实就是非叶结点之间形成的连通块,不妨称为新树。我们现在只需要把叶结点(度数为 1 的点)拼到父亲上面即可得到原树。

我们对于每个非叶结点,求出在新树中距离 <= 1 的点集,称为新邻集。

原图中叶结点原邻集 = 与该叶结点父亲相同的叶结点集合 + 父亲的新邻集。

如果新树只有一条边,此时两个点的新邻集相同,因此需要特判(父亲不同的叶结点分别连两个点)。

否则,每个点的新邻集互相不同。因此我们可以把原邻集与新树取交集,找到那个唯一的父亲。

不过注意到,当某个点在新树中度数为 1,此时它的原邻集与新树取交集也会对应一个新邻集。

此时该点一定连向某个原图中叶结点,且该叶结点的原邻集是该点的真子集。因此我们再判一下是否存在某个点的原邻集是当前点的原邻集的真子集即可。

复杂度 \(O(\frac{n^3}{\omega})\),实际上跑得挺快。

@accepted code@

#include <bitset>
#include <cstdio>
#include <algorithm>
using namespace std; int tag[1005], n;
bitset<1005>a[1005], b[1005], c[1005], t, p;
int main() {
scanf("%d", &n);
for(int i=1;i<=n;i++) {
int k, x; scanf("%d", &k);
for(int j=1;j<=k;j++)
scanf("%d", &x), b[i][x] = 1;
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++) {
t = (b[i] & b[j]);
// printf("%d %d : %d\n", i, j, t.count());
if( t.count() == 2 ) {
int x = t._Find_first(); t[x] = 0;
int y = t._Find_first();
a[x][y] = a[y][x] = 1;
}
}
t = 0;
for(int i=1;i<=n;i++)
if( a[i].count() ) tag[i] = -1, t[i] = 1;
for(int i=1;i<=n;i++) a[i][i] = 1;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if( a[i][j] ) printf("%d %d\n", i, j);
if( t.count() == 0 ) {
for(int i=2;i<=n;i++)
printf("%d %d\n", 1, i);
}
else if( t.count() == 2 ) {
for(int x=1;x<=n;x++) {
for(int y=x+1;y<=n;y++) {
if( a[x][y] ) {
bool flag = false;
for(int i=1;i<=n;i++)
if( b[i].count() != n ) {
for(int j=1;j<=n;j++)
if( b[i][j] ) {
if( tag[j] == 1 ) break;
else if( tag[j] != -1 ) {
printf("%d %d\n", flag ? y : x, j);
tag[j] = 1;
}
}
flag = true;
}
}
}
}
}
else {
for(int i=1;i<=n;i++) {
c[i] = (b[i] & t);
bool flag = true;
for(int j=1;j<=n;j++)
if( b[i] == b[j] ) {
if( j < i ) {
flag = false;
break;
}
}
else if( (b[i] & b[j]) == b[j] ) {
flag = false;
break;
}
if( flag ) {
for(int j=1;j<=n;j++)
if( c[i] == a[j] ) {
for(int k=1;k<=n;k++)
if( b[i][k] && tag[k] == 0 )
printf("%d %d\n", j, k), tag[k] = 1;
break;
}
}
}
}
}

@details@

bitset 真好用。

一开始还想着二分图匹配找每个集合对应原树中哪一个点,不过发现找出来并不会更好做。

@codefoces - 566E@ Restoring Map的更多相关文章

  1. Codeforces.566E.Restoring Map(构造)

    题目链接 \(Description\) 对于一棵树,定义某个点的邻居集合为所有距离它不超过\(2\)的点的集合(包括它自己). 给定\(n\)及\(n\)个点的邻居集合,要求构造一棵\(n\)个点的 ...

  2. Codeforces 566E - Restoring Map(bitset 优化构造)

    Codeforces 题目传送门 & 洛谷题目传送门 本来说好的不做,结果今早又忍不住开了道题/qiao 我们称度为 \(1\) 的点为叶节点,度大于 \(1\) 的点为非叶节点. 首先考虑如 ...

  3. CF566E Restoring Map

    题意:乱序给你树上的每一个节点与之相距<=2的节点集合(并不知道这具体是哪个节点). 还原整棵树. 标程: #include<bits/stdc++.h> #define P pai ...

  4. WC2021 题目清单

    Day2 上午 <IOI题型与趣题分析> 来源 题目 完成情况 备注 IOI2002 Day1T1 Frog 已完成 IOI2002 Day1T2 Utopia IOI2002 Day1T ...

  5. Tree Restoring

    Tree Restoring Time limit : 2sec / Memory limit : 256MB Score : 700 points Problem Statement Aoki lo ...

  6. Backing Up and Restoring HBase Data

    There are two strategies for backing up HBase:1> Backing it up with a full cluster shutdown2> ...

  7. PatentTips – GPU Saving and Restoring Thread Group Operating State

    BACKGROUND OF THE INVENTION The present invention relates generally to single-instruction, multiple- ...

  8. mapreduce中一个map多个输入路径

    package duogemap; import java.io.IOException; import java.util.ArrayList; import java.util.List; imp ...

  9. .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法

    .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法 0x00 为什么需要Map(MapWhen)扩展 如果业务逻辑比较简单的话,一条主管道就够了,确实用不到 ...

随机推荐

  1. Android gradle 自定义插件

    Gradle 的插件有三种打包方式: 构建脚本:插件逻辑写在 build.gradle 中,适用于逻辑简单的任务,但是该方式实现的插件在该构建脚本之外是不可见的,只能用于当前脚本. buildSrc项 ...

  2. Istio-架构

    读书笔记整理 工作机制:分为控制面和数据面 控制面:Pilot, Mixer(接收来自Envoy上报的数据), Citadel(证书和密钥管理) 数据面:Envoy 工作流程: 自动注入 应用程序启动 ...

  3. Spring MVC必须知道的执行流程

    Spring MVC的执行流程 一.名词解释 1.前端控制器(DispatcherServlet) 接收请求,响应结果,相当于转发器,中央处理器 2.处理器映射器(HandlerMapping) 根据 ...

  4. CSS3和HTML5头部定位自用

    body{ max-width: 540px; min-width: 320px; margin: 0 auto; font: normal 14px/1.5 tahoma; color: #000; ...

  5. excel导入mysql数据

    excel加载mysql数据 1.第一步,选择从mysql导入数据 2.单击会出现弹框: 3.可能有的同学的,这里缺少插件,例如: 4.去下载 这个 插件安装即可.https://dev.mysql. ...

  6. 人机协同与AI能力训练

    我们进行<中台战略>一书的第三期分享. “人机融合是解决aI机器人冷启动的绝佳解决方案,我们这里引入了一个应答满意度的指标,每一个咨询应答都对应一个应答满意度.当消费者应该回答选择转入人工 ...

  7. 使用 IdentityService4 集成小程序登录一种尝试

    1 场景介绍 主要业务是通过 App 承载,在 App 中可以注册和登录,为了更好的发展业务引入了微信小程序,于是如何让这两个入口的用户互通便成了需要解决的问题. 看了一下其它 App 大致地思路是两 ...

  8. winform 数据(双向)绑定 快速更新实体

    写点东西感觉特别累,也已经很久没写了. 最近在做一个winform类型的系统,其涉及到大量的表,有些表又包含大量的字段,所以当添加.更新实体时便会十分的繁琐,一方面:需要把界面上的字段绑定到对应的实体 ...

  9. 痞子衡嵌入式:MCUBootUtility v2.3发布,这次不再放过任何一款Flash

    -- 痞子衡的 NXP-MCUBootUtility 开源项目自2018年8月27日第一笔提交至今已有21个月,目前累计代码已近50000行.相信这个工具为大家开发 i.MXRT 项目提供了一些便利, ...

  10. Dubbo源码阅读-服务导出

    Dubbo服务导出过程始于Spring容器发布刷新事件,Dubbo在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可分为三个部分,第一部分是前置工作,主要用于检查参数,组装URL.第二部分是导出服 ...