题解:P11486 「Cfz Round 5」Mata rainen
首先先考虑什么时候是无解的。数据无解当且仅当有环(重边也算作环)。树是没有环的,所以在树上把环压成链必然会导致路径重复。
现在我们可以想想怎么构造这棵树。下面默认数据中没有环。
首先,当
但是,如果用点对直接建成树,可能会有一些点没有被加进树里。例如:
6 3
1 2
1 3
1 5
解决方法:我们发现,把一条边变成一条链也是可以的。那么这些没有加进树的点就可以直接塞进某一条边中:
Yes
1 2
1 3
1 4
4 6
6 5
这里在边 1 5 中塞进了点 4 和 6,变成了后三行。
下一个问题:如果输入的点对之间不连通怎么办?(就像样例1)
参考刚才的方法。直接将后来的树的根塞进前面树的一条边中。
#include <bits/stdc++.h>
using namespace std;
namespace llamn{
int n,m,i,j,k,t1,t2;
struct eggy{int to,lst;};
struct eggy e[600100];
int h[300100], cnt;
void add(int u, int w);
bitset <300100> xv;
void dfs(int d, int f); //判断环
bitset<300100> ved,t;
int ped, ansl[300100], ansr[300100];
void adde(int v); //在准备输出的树中的最新一条边中插入点v
void dfs_ans(int d, int f);
int s[300100], tp;
#define mk_pair(a,b) ((((long long)(a))<<30)+(b))
unordered_set<long long> qq;
int main()
{
scanf("%d%d",&n,&m);
for (i = 1; i <= m; i++)
{
scanf("%d%d",&t1,&t2);
t[t1] = 1, t[t2] = 1;
add(t1,t2); add(t2,t1);
if (qq.find(mk_pair(t1,t2)) != qq.end()) //判断重边
{
puts("No");
return 0;
}qq.insert(mk_pair(t1,t2));
}
for (i = 1; i <= n; i++)
{
if (!xv[i]) dfs(i,0); //上下这两行没有关联
if (!t[i]) s[++tp] = i; //记录哪些点从未出现
}puts("Yes");
for (i = 1; i <= n; i++)
{
if (!t[i]) continue;
if (ved[i]) continue;
if (ped) adde(i); //把新树的树根塞进上一棵树的边中
dfs_ans(i,-1);
}for (i = 1; i <= tp; i++) //把没有出现的点塞进一条边中
{
adde(s[i]);
}
for (i = 1; i <= ped; i++)
{
printf("%d %d\n",ansl[i], ansr[i]);
}
return 0;
}
void add(int u, int w)
{
e[++cnt].to = w;
e[cnt].lst = h[u];
h[u] = cnt;
}void dfs(int d, int f)
{
xv[d] = 1;
for (int i = h[d]; i; i = e[i].lst)
{
if (e[i].to == f) continue;
if (xv[e[i].to])
{
puts("No");
exit(0);
}dfs(e[i].to,d);
}
}void dfs_ans(int d, int f)
{
ved[d] = 1;
for (int i = h[d]; i; i = e[i].lst)
{
if(e[i].to == f) continue;
ansl[++ped] = d; ansr[ped] = e[i].to;
dfs_ans(e[i].to,d);
}
}void adde(int v)
{
t1 = ansr[ped];
ansr[ped] = v;
ansl[++ped] = v;
ansr[ped] = t1;
}
}int main(){llamn::main();return 0;}
/*
元梦树上元梦果,元梦树下我放火
*/