题解:AT_arc211_d [ARC211D] Michishirube
不错的算法、构造的综合思维题,可能这种做熟练了也感觉偏典?
本题概要题意:给定一张简单无向连通图,指定节点
简单分析性质,很难不发现,标上每种颜色的边数均为
分析上述性质,我们考虑先对蓝色跑一种合法方案(生成树)。为防止横叉边的干扰,在这里我们采用 DFS 生成树。此时我们要做到利用树边向下、非树边反祖来使所有点都能到达
继续考虑如何达到这一效果。对于节点
如何走到最小深度呢?显然可以从下到上遍历树,记录当前节点所能到的最小深度,若儿子节点能到达更小深度,则将箭头指向能到达最小深度的儿子节点,否则指向该点所连非树边中能到深度最小的节点。
现在我们获得了构造方案,下证直接以这种方式判断出有无解的充要性。在这种构造方式下,无解当且仅当某节点无法跳到深度足够小处,而这表明该节点有一条必经树边才能向上到达深度更小以致能到达
::::success[code]
#include<bits/stdc++.h>
using namespace std;
int n,m,pa[200010],low[200010],p[200010],d[200010],cntt;
//low 所能到达最小深度,p 黄箭头指向
vector <int> b[200010],son[200010],fs[200010],fp[200010];
//fs 非树边,fp 反黄边
bool vis[200010];
//建树
void dfs1(int id)
{for(int i=0;i<b[id].size();i++)
{if(!d[b[id][i]])
{pa[b[id][i]]=id;son[id].push_back(b[id][i]);
d[b[id][i]]=d[id]+1;dfs1(b[id][i]);
}
else if(b[id][i]!=pa[id])
fs[id].push_back(b[id][i]);
}
}
//定向
void dfs2(int id)
{int _min=d[id]+1,min_id;
for(int i=0;i<son[id].size();i++)
{dfs2(son[id][i]);
if(low[son[id][i]]<_min)
{_min=low[son[id][i]];
min_id=son[id][i];
}
}
for(int i=0;i<fs[id].size();i++)
{if(d[fs[id][i]]<_min)
{_min=d[fs[id][i]];
min_id=fs[id][i];
}
}
low[id]=_min;
if(p[id]==0)
{p[id]=min_id;fp[min_id].push_back(id);}
}
//判可行
void dfs3(int id)
{vis[id]=true;cntt++;
for(int i=0;i<fp[id].size();i++)
{if(!vis[fp[id][i]])
dfs3(fp[id][i]);
}
}
int main()
{cin>>n>>m;
for(int i=1,u,v;i<=m;i++)
{cin>>u>>v;
b[u].push_back(v);b[v].push_back(u);
}
d[1]=1;
dfs1(1);
for(int i=1;i<=n;i++)
low[i]=d[i];
int id=2;
while(pa[id]!=0)
{p[pa[id]]=id;fp[id].push_back(pa[id]);
id=pa[id];
}
dfs2(1);
dfs3(2);
if(cntt!=n) {cout<<"No";return 0;}
cout<<"Yes\n";
cout<<p[1]<<'\n'<<pa[2]<<'\n';
for(int i=3;i<=n;i++)
cout<<pa[i]<<' '<<p[i]<<'\n';
}