题目大意:给出一个无向连通图,让你筛选出一棵生成树,使得编号为1的点,度为D。
一开始想都没想,直接照F1的样子写了一发,1号点先选d条边,其他点选边的时候不能选连接1号点的边,用并查集维护树。
进一步思考,可能出现1号点边不够d的情况,即使1号点边足够d,因为后面不能再选和1相连的边,为保证树的连通,关节边必须先选(即桥),如果关节边的数量多余d,以1号点为根结点的dfs树必定会丢失几个子树。
因此要在有合法解的情况下先选出关节边。
(在 一个该死的细节卡了7发后我居然开始怀疑tarjan算法的正确性而改用dfs去WA我的第8发…)
#include<iostream>
using namespace std;
#include<map>
#include<string.h>
#include<queue>
const int maxn = 2e5+30;
vector<int> g[maxn];
vector<int> h[maxn];
int p[maxn];
int findx(int x){
return x==p[x]?x:p[x]=findx(p[x]);
}
int low[maxn],dfn[maxn],cnt,res;
int num[maxn];
void tarjan(int s,int fa){
low[s]=dfn[s]=++res;
for(int i = 0;i < g[s].size(); i++){
if(g[s][i]==fa) continue ;
if(!dfn[g[s][i]]) tarjan(g[s][i],s);
else low[s]=min(low[s],dfn[g[s][i]]);
if(dfn[s]<low[g[s][i]])
h[s].push_back(g[s][i]);
}
}
int main(){
int n,m,d;
scanf("%d%d%d",&n,&m,&d);
int x,y,ans;
for(int i = 1; i <= n; i++) p[i] = i;
for(int i = 1; i <= m; i++){
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
tarjan(1,-1);
if(g[1].size()<d||h[1].size()>d){
puts("NO");
return 0;
}
puts("YES");
int i;
for(i = 0; i < h[1].size() && d>0; i++){
int fx=findx(1),fy=findx(h[1][i]);
p[fy]=fx;
d--;
printf("%d %d\n",1,h[1][i]);
}
for(i = 0; i < g[1].size() && d; i++){
int fx=findx(1),fy=findx(g[1][i]);
if(fy!=fx){
p[fy]=fx;
printf("%d %d\n",1,g[1][i]);
d--;
}
}
for(int i = 2; i <= n; i++){
for(int j = 0; j < g[i].size(); j++){
if(g[i][j]==1) continue;
int fx=findx(i),fy=findx(g[i][j]);
if(fx!=fy){
printf("%d %d\n",i,g[i][j]);
p[fy]=fx;
}
}
}
}