P2860 [USACO06JAN]Redundant Paths G

在这里插入图片描述
题意:给你一个无向图,要求往图中增加最少的边数 a n s ans ans,使得这个图中任意的两个点都有两条以上的路径.
首先注意到任意两个点都有两条以上的路径这个性质,这是边双连通的定义,需要想到.
那么,我们首先把这个图的边双求出来,对于每一块 e c c ecc ecc,都是由桥连接的,它们都只有一条路径。没有圈的连通图,这满足树的定义,所以执行完边双得到的是一颗树.我们想在树中添加若干条边,使得整个树变成一个 e c c ecc ecc
考虑下哪里出了问题,要想成为一个 e c c ecc ecc,那必然每个节点到其他节点有两条路径,类似圈.问题出现在叶子上,叶子之间是必须有连边的,否则路径只有叶子-> L C A LCA LCA->叶子.
那么这样做是最少的吗,不是.考虑以下情况:
在这里插入图片描述
在这里插入图片描述
只需添加两条边,叶子有4个.这启示我们靠叶子的方向思考,
首先只要叶子存在,由于只有一条出边,所以肯定不会存在 e c c ecc ecc.所以我们希望叶子消失,如果有 l e a f leaf leaf个叶子,可以考虑两两连边 l e a f / 2 leaf/2 leaf/2消掉叶子.
当叶子是奇数的时候,我们仍然要消去,随便连一个就行,就是 ( l e a f + 1 ) / 2 (leaf+1)/2 (leaf+1)/2
那么没有叶子之后,就变成 e c c ecc ecc了吗,似乎是的,那样保证每一个点至少有两条边连接。因为图是连通的,而且每一个点都至少有两条边
那么 u − > v u->v u>v既可以从一条边到,又可以从另外一条边抵达.

/*
I don't wanna be left behind,Distance was a friend of mine.
Catching breath in a web of lies,I've spent most of my life.
Catching my breath letting it go turning my cheek for the sake of this show
Now that you know this is my life I won't be told what's supposed to be right
Catch my breath no one can hold me back I ain't got time for that.
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
const int INF = 1e9+7;
typedef long long ll;
typedef pair<int,int> pii;
#define all(a) (a).begin(), (a).end()
#define pb(a) push_back(a)
//Tarjan类最好用前向星
int to[maxn],head[maxn],nxt[maxn],tot=1;
void add(int u,int v){
	to[++tot] = v,nxt[tot] = head[u],head[u] = tot;
}
int dfn[maxn],low[maxn],dfs_clock;
bool bridge[maxn*2];
void dfs1(int u,int i_edge){
	dfn[u] = low[u] = ++dfs_clock;
	for(int i=head[u];i;i=nxt[i]){
		int v = to[i];
		if(!dfn[v]){
			dfs1(v,i);
			low[u] = min(low[u],low[v]);
			if(low[v]>dfn[u]) bridge[i] = bridge[i^1] = true;
		}
		else if(i!=(i_edge^1)) low[u] = min(low[u],dfn[v]);
	}
}
int ecc_cnt=0,ecc[maxn];
void DFS(int u){
	ecc[u] = ecc_cnt;
	for(int i=head[u];i;i=nxt[i]){
		int v = to[i];
		if(ecc[v]||bridge[i]) continue;
		DFS(v);
	}
}
int deg[maxn*2];
int main(){
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n,m;cin>>n>>m;
	vector<pii> edge;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;add(u,v);add(v,u);
		edge.push_back({u,v});
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]) dfs1(i,0);
	}
	for(int i=1;i<=n;i++){
		if(!ecc[i]){
			ecc_cnt++;DFS(i);
		}
	}
//	cout<<ecc_cnt<<"\n";
	for(auto [u,v] : edge){
		if(ecc[u]!=ecc[v]){
			deg[ecc[u]]++;
			deg[ecc[v]]++;
		}
	}
	int leaf = 0;
	for(int i=1;i<=ecc_cnt;i++){
		if(deg[i]==1) leaf++;
	}
	cout<<(leaf+1)/2<<"\n";
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

minato_yukina

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值