CF_div3_problem_F2:Spanning Tree with One Fixed Degree(基本图论+tarjan+并查集)

题目大意:给出一个无向连通图,让你筛选出一棵生成树,使得编号为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;
   			}
  		}
 	}
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/88679916
今日推荐