构造一张二分图,左边是$n$个点,右边是$n-1$个集合,按照点属于集合连边

定义一组匹配的意义,即说明该点的父亲在该集合中选择

利用dinic求出二分图的最大匹配,若不为$n-1$则无解,否则考虑如何去构造一组解:

考虑左边剩下的未参与匹配的点$x$,将其作为根,并将所有含有$x$的集合所匹配的点都作为$x$的儿子,重复此过程(对于其他点,要加上一个“未被选择过”)

事实上,这个过程可以看作从源点出发在残余网络上的一棵bfs树,合法当且仅当能bfs到所有点

(能从$x$走到对应集合当且仅当其匹配的不是该集合,能从一个集合走到$y$当且仅当$y$匹配了该集合,同时$y$一定不会去选择其所匹配的集合中的点,因为一定被其父亲选择完毕)

下面,我们来证明若某一种匹配方式不能做到,则其余都不行:

由于根是任意的,换言之这个点作为根如果不行,其余点也都不行,因此确定了根

考虑调整匹配方案,一定可以通过若干次对一个序列的循环(通过将调整建为一张图可以证明)

若轮换前这一个集合中存在一个点能被搜到,按照轮换的顺序其余点都能被搜到,因此轮换前所有点都不能被搜到,即是一个内部的交换,没有意义

总复杂度为$o(n\sqrt{n})$,可以通过

 1 #include<bits/stdc++.h>
2 using namespace std;
3 #define N 100005
4 #define oo 0x3f3f3f3f
5 struct ji{
6 int nex,to,len;
7 }edge[N<<3];
8 queue<int>q;
9 pair<int,int>ans[N];
10 int E,n,x,y,tot,head[N<<1],work[N<<1],d[N<<1];
11 void add(int x,int y,int z){
12 edge[E].nex=head[x];
13 edge[E].to=y;
14 edge[E].len=z;
15 head[x]=E++;
16 if (E&1)add(y,x,0);
17 }
18 bool bfs(){
19 memset(d,oo,sizeof(d));
20 d[0]=0;
21 q.push(0);
22 while (!q.empty()){
23 int k=q.front();
24 q.pop();
25 for(int i=head[k];i!=-1;i=edge[i].nex)
26 if ((edge[i].len)&&(d[edge[i].to]==oo)){
27 d[edge[i].to]=d[k]+1;
28 q.push(edge[i].to);
29 }
30 }
31 return d[2*n]<oo;
32 }
33 int dfs(int k,int s){
34 if (k==2*n)return s;
35 for(int &i=work[k];i!=-1;i=edge[i].nex)
36 if ((edge[i].len)&&(d[edge[i].to]==d[k]+1)){
37 int p=dfs(edge[i].to,min(s,edge[i].len));
38 if (p){
39 edge[i].len-=p;
40 edge[i^1].len+=p;
41 return p;
42 }
43 }
44 return 0;
45 }
46 int dinic(){
47 int k,ans=0;
48 while (bfs()){
49 memcpy(work,head,sizeof(work));
50 while (k=dfs(0,oo))ans+=k;
51 }
52 return ans;
53 }
54 void bfs_build(){
55 memset(d,0,sizeof(d));
56 for(int i=head[0];i!=-1;i=edge[i].nex)
57 if (edge[i].len){
58 q.push(edge[i].to);
59 d[edge[i].to]=edge[i].to;
60 }
61 while (!q.empty()){
62 int k=q.front();
63 q.pop();
64 for(int i=head[k];i!=-1;i=edge[i].nex)
65 if ((edge[i].to)&&(edge[i].len)&&(!d[edge[i].to])){
66 if (edge[i].to>n)d[edge[i].to]=d[k];
67 else{
68 tot++;
69 ans[k-n]=make_pair(d[k],edge[i].to);
70 d[edge[i].to]=edge[i].to;
71 }
72 q.push(edge[i].to);
73 }
74 }
75 }
76 int main(){
77 scanf("%d",&n);
78 memset(head,-1,sizeof(head));
79 for(int i=1;i<=n;i++)add(0,i,1);
80 for(int i=1;i<n;i++){
81 scanf("%d",&x);
82 add(i+n,2*n,1);
83 for(int j=1;j<=x;j++){
84 scanf("%d",&y);
85 add(y,i+n,1);
86 }
87 }
88 if (dinic()<n-1){
89 printf("-1");
90 return 0;
91 }
92 bfs_build();
93 if (tot!=n-1)printf("-1");
94 else{
95 for(int i=1;i<n;i++)
96 printf("%d %d\n",ans[i].first,ans[i].second);
97 }
98 }

[atAGC029F]Construction of a tree的更多相关文章

  1. @atcoder - AGC029F@ Construction of a tree

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定 N - 1 个 {1, 2, ..., N} 的子集,第 ...

  2. AT4505-[AGC029F]Construction of a tree【构造题,hall定理,网络流】

    正题 题目链接:https://www.luogu.com.cn/problem/AT4505 题目大意 给出\(n\)个点和\(n-1\)个点集\(U_i\),每个点集中选择两个点连边使得该图是一棵 ...

  3. phylogeny analysis

    Multiple Alignment: MUSCLE ProbCons T-Coffee ClustalW Alignment curation: Gblocks Remove positions w ...

  4. 【AtCoder】AGC029(A-E)

    A - Irreversible operation 题解 把每个B后面的W个数累加起来即可 代码 #include <bits/stdc++.h> #define fi first #d ...

  5. JS进阶 - 浏览器工作原理

    一.浏览器的结构 浏览器的主要组件为: 用户界面 - 包括地址栏.前进/后退按钮.书签菜单等.除了浏览器主窗口(显示页面),其他部分都属于用户界面. 浏览器引擎 - 在用户界面和渲染引擎之间传送指令. ...

  6. Bounding Volume Hierarchy BVH in OpenCASCADE

    Bounding Volume Hierarchy BVH in OpenCASCADE eryar@163.com Abstract. Bounding Volume Hierarchy(BVH) ...

  7. WC2021 题目清单

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

  8. 数据结构 - Codeforces Round #353 (Div. 2) D. Tree Construction

    Tree Construction Problem's Link ------------------------------------------------------------------- ...

  9. STL---Codeforces675D Tree Construction(二叉树节点的父亲节点)

    Description During the programming classes Vasya was assigned a difficult problem. However, he doesn ...

随机推荐

  1. vue3 element-plus 配置json快速生成table列表组件,提升生产力近500%(已在公司使用,持续优化中)

    ️本文为博客园首发文章,未获授权禁止转载 大家好,我是aehyok,一个住在深圳城市的佛系码农‍♀️,如果你喜欢我的文章,可以通过点赞帮我聚集灵力️. 个人github仓库地址: https:gith ...

  2. Conda 命令

    Conda 命令 1 检验当前conda的版本 conda -V 2 conda常用的命令 查看已有的虚拟环境 conda env list 创建虚拟环境和删除虚拟环境 anaconda命令创建pyt ...

  3. UOJ 2021 NOI Day2 部分题解

    获奖名单 题目传送门 Solution 不难看出,若我们单个 \(x\) 连 \((0,x),(x,0)\),两个连 \((x,y),(y,x)\) ,除去中间过对称轴的一个两个组,就是找很多个欧拉回 ...

  4. [spring-rabbit]自动配置原理

    1 一个简单的示例 在Spring Boot项目中使用spring-rabbit时,需要经过以下几个步骤: 引入依赖. 配置基本连接信息. 创建消息发布者,并发送消息. 创建消息消费者,监听消息并处理 ...

  5. 简单的 Go 入门教程

    Go(又称 Golang )是 Google 开发的一种静态强类型.编译型.并发型,并具有垃圾回收功能的编程语言 Docker 和 Kubernetes 都是使用 Go 进行开发的,这几年 Go 越来 ...

  6. /usr/bin/python^M: bad interpreter: No such file or directory

    利用如下命令查看文件格式 :set ff 或 :set fileformat 可以看到如下信息 fileformat=dos 或 fileformat=unix 利用如下命令修改文件格式 :set f ...

  7. OO第二单元——多线程(电梯)

    OO第二单元--多线程(电梯) 综述 第二单元的三次联系作业都写电梯,要求逐步提高,对于多线程的掌握也进一步加深.本次作业全部都给出了输入输出文件,也就避免了正则表达式判断输入输出是否合法的问题. 第 ...

  8. FastAPI 学习之路(三十四)数据库多表操作

    之前我们分享的是基于单个的数据库表的操作,我们在设计数据库的时候也设计了跨表,我们可以看下数据库的设计. class User(Base): __tablename__ = "users&q ...

  9. 玩转C语言链表-链表各类操作详解

    链表概述 链表是一种常见的重要的数据结构.它是动态地进行存储分配的一种结构.它可以根据需要开辟内存单元.链表有一个"头指针"变量,以head表示,它存放一个地址.该地址指向一个元素 ...

  10. 第12课 OpenGL 显示列表

    显示列表: 想知道如何加速你的OpenGL程序么?这一课将告诉你如何使用OpenGL的显示列表,它通过预编译OpenGL命令来加速你的程序,并可以为你省去很多重复的代码. 这次我将教你如何使用显示列表 ...