@atcoder - AGC029F@ Construction of a tree
@description@
给定 N - 1 个 {1, 2, ..., N} 的子集,第 i 个为 Ei。
请构造 N - 1 条边 (u1, v1), (u2, v2), ... 使得 ui ∈ Ei 且 vi ∈ Ei,满足这 N - 1 条边构成一棵树。
@solution@
好神的题。
构造一个二分图,第 i 条边对应的点连向 Ei 中所有的点。
有解的一个充分条件是大小为 k 的边集合能够连到点集合大小 > k(否则肯定会连成环)。
注意到这个和 hall 定理挺像的。记 N(S) 表示 S 的邻集,hall 定理描述的是 |S| <= |N(S)| 等价于二分图有完美匹配,而上述充分条件为 |S| < |N(S)|。
如何把它和 hall 定理联系起来呢?如果我们把 N 个点任意去掉一个点,那么应该有 |S| <= |N(S)|。
也就是 N 个点每一个点都不是二分图的必需点,这样就能推出 |S| < |N(S)| 的结论。
跑个 dinic,左边右点。此时如果能从点 i 到达汇点 t,则 i 不是必需点。
也就是以 t 为根沿逆向边建一棵可到达 t 的生成树,如果 N 个点都在树上,则合法。
注意此时这棵生成树就是我们想要构造的树。可以发现 N 个点都在树上时 N-1 条边对应的点也在树上,而 N-1 条边一定只会和两条树边连通,就是这条边对应的两个端点。
@accepted code@
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 100000;
namespace FlowGraph{
const int MAXV = 2*MAXN;
const int MAXE = 10*MAXN;
const int INF = (1 << 30);
struct edge{
int to, flow, cap;
edge *nxt, *rev;
}edges[MAXE + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt = edges;
void addedge(int u, int v, int c) {
edge *p = (++ecnt), *q = (++ecnt);
p->to = v, p->cap = c, p->flow = 0;
p->nxt = adj[u], adj[u] = p;
q->to = u, q->cap = 0, q->flow = 0;
q->nxt = adj[v], adj[v] = q;
p->rev = q, q->rev = p;
}
int s, t;
int fa[MAXV + 5], dis[MAXV + 5];
int que[MAXV + 5], hd, tl;
bool relabel() {
for(int i=s;i<=t;i++)
dis[i] = t + 5, cur[i] = adj[i];
dis[que[hd = tl = 1] = t] = 0;
while( hd <= tl ) {
int x = que[hd++];
for(edge *p=adj[x];p;p=p->nxt) {
if( dis[p->to] > dis[x] + 1 && p->rev->cap > p->rev->flow )
dis[p->to] = dis[x] + 1, fa[p->to] = x, que[++tl] = p->to;
}
}
return !(dis[s] == t + 5);
}
int aug(int x, int tot) {
if( x == t ) return tot;
int sum = 0;
for(edge *&p=cur[x];p;p=p->nxt) {
if( p->cap > p->flow && dis[p->to] + 1 == dis[x] ) {
int del = aug(p->to, min(tot - sum, p->cap - p->flow));
p->flow += del, p->rev->flow -= del, sum += del;
if( sum == tot ) break;
}
}
return sum;
}
int max_flow(int _s, int _t) {
int flow = 0; s = _s, t = _t;
while( relabel() )
flow += aug(s, INF);
return flow;
}
}
int a[MAXN + 5], b[MAXN + 5];
int main() {
int N; scanf("%d", &N);
for(int i=1;i<N;i++) {
int c; scanf("%d", &c);
for(int j=1;j<=c;j++) {
int w; scanf("%d", &w);
FlowGraph::addedge(N + i, w, 1);
}
}
int s = 0, t = N + N - 1 + 1;
for(int i=1;i<N;i++) FlowGraph::addedge(s, N + i, 1);
for(int i=1;i<=N;i++) FlowGraph::addedge(i, t, 1);
int f = FlowGraph::max_flow(s, t);
if( f == N - 1 ) {
FlowGraph::relabel();
for(int i=1;i<t;i++)
if( FlowGraph::dis[i] == t + 5 ) {
puts("-1");
return 0;
}
for(int i=1;i<N;i++) a[i] = FlowGraph::fa[N + i];
for(int i=1;i<=N;i++) b[FlowGraph::fa[i] - N] = i;
for(int i=1;i<N;i++) printf("%d %d\n", a[i], b[i]);
}
else puts("-1");
}
@details@
我好菜啊。
这种判定性 + 构造性问题以后又有一种新的思路了:是否所有子集满足一定条件就合法。
@atcoder - AGC029F@ Construction of a tree的更多相关文章
- 【AtCoder AGC023F】01 on Tree(贪心)
Description 给定一颗 \(n\) 个结点的树,每个点有一个点权 \(v\).点权只可能为 \(0\) 或 \(1\). 现有一个空数列,每次可以向数列尾部添加一个点 \(i\) 的点权 \ ...
- AtCoder 2376 Black and White Tree
D - Black and White Tree Time limit : 2sec / Memory limit : 256MB Score : 900 points Problem Stateme ...
- AtCoder Grand Contest 010 F - Tree Game
题目传送门:https://agc010.contest.atcoder.jp/tasks/agc010_f 题目大意: 给定一棵树,每个节点上有\(a_i\)个石子,某个节点上有一个棋子,两人轮流操 ...
- AtCoder Grand Contest 018 D - Tree and Hamilton Path
题目传送门:https://agc018.contest.atcoder.jp/tasks/agc018_d 题目大意: 给定一棵\(N\)个点的带权树,求最长哈密顿路径(不重不漏经过每个点一次,两点 ...
- AtCoder Grand Contest 005 C - Tree Restoring
题目传送门:https://agc005.contest.atcoder.jp/tasks/agc005_c 题目大意: 给定一个长度为\(N\)的整数序列\(A_i\),问能否构造一个\(N\)个节 ...
- Atcoder D - Black and White Tree(树dp+博弈)
题目链接:http://agc014.contest.atcoder.jp/tasks/agc014_d 题意:有一棵树先手涂白色,后手涂黑色,直到不能再涂为止.涂完后再把所有黑色直接相邻的白色都变成 ...
- AtCoder AGC014E Blue and Red Tree (启发式合并)
题目链接 https://atcoder.jp/contests/agc014/tasks/agc014_e 题解 完了考场上树剖做法都没想到是不是可以退役了... 首先有一个巨难写的据说是\(O(n ...
- [AtCoder Regular Contest 083] Bichrome Tree
树形DP. 每个点有两个属性:黑色点的权值和,白色点权值和,一个知道另一个也一定知道. 因为只要子树的和它相等的点得权值和不超过x[u],u点的权值总能将其补齐. 设计状态f[u]表示以u为根的子树, ...
- AT4505-[AGC029F]Construction of a tree【构造题,hall定理,网络流】
正题 题目链接:https://www.luogu.com.cn/problem/AT4505 题目大意 给出\(n\)个点和\(n-1\)个点集\(U_i\),每个点集中选择两个点连边使得该图是一棵 ...
随机推荐
- 你还不懂 Tomcat 的优化吗?
前言 Tomcat 服务器是一个开源的轻量级Web应用服务器,在中小型系统和并发量小的场合下被普遍使用,是开发和调试Servlet.JSP 程序的首选.相信大家对于 Tomcat 已经是非常熟悉了,本 ...
- TP5.0验证器使用方法
比如我要在分类做一个验证器,首先要在你的后台模块先建立一个文件夹,比如我的后台模块是admin,那么你要在admin里面建立一个validate的文件夹然后再建立一个对应的php文件如下图 文件里面的 ...
- CSS 伪选择器 focus-within 介绍
CSS中的 :focus-within 伪选择器可有点"不寻常",尽管它的名称看上去很简单,而且相当直观.但它的解释是:选择一个包含具有:focus 的任何子元素的元素.有点绕是不 ...
- 那些面试官必问的JAVA多线程和并发面试题及回答
Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环 ...
- javascript -字符串常用方法
let str ="stRing" toUpperCase / toLowerCase 大小写 toUpperCase()把一个字符串全部变为大写; toLowerCas ...
- 看板 | 漫话之减少WIP(在制品)
传统的流水线生产模式中,生产流程按生产程序进行划分,而各部分因动作的难度或复杂程度的差异导致用时不同. 例如:一个产品进行组装用时2分钟,完成装箱工作只需10秒.在这种情况下,组装动作用时长.产出慢, ...
- Java实现 LeetCode 804 唯一摩尔斯密码词 (暴力)
804. 唯一摩尔斯密码词 国际摩尔斯密码定义一种标准编码方式,将每个字母对应于一个由一系列点和短线组成的字符串, 比如: "a" 对应 ".-", " ...
- Java 第十一届 蓝桥杯 省模拟赛 洁净数
洁净数 小明非常不喜欢数字 2,包括那些数位上包含数字 2 的数.如果一个数的数位不包含数字 2,小明将它称为洁净数. 请问在整数 1 至 n 中,洁净数有多少个? 输入格式 输入的第一行包含一个整数 ...
- Java实现 LeetCode 515 在每个树行中找最大值
515. 在每个树行中找最大值 您需要在二叉树的每一行中找到最大的值. 示例: 输入: 1 / \ 3 2 / \ \ 5 3 9 输出: [1, 3, 9] /** * Definition for ...
- Java实现 蓝桥杯VIP 算法训练 新生舞会
问题描述 新生舞会开始了.n名新生每人有三个属性:姓名.学号.性别.其中,姓名用长度不超过20的仅由大小写字母构成的字符串表示,学号用长度不超过10的仅由数字构成的字符串表示,性别用一个大写字符'F' ...