tarjan算法实践(https://codeforces.com/problemset/problem/1986/F)

题目简介:

已知一个无向联通图,可以去掉一条边,使得任意可互相到达的点对(a,b)数量最少

 

题目不简介(可以直接跳):

You are given a connected undirected graph, the vertices of which are numbered with integers from 11 to n𝑛. Your task is to minimize the number of pairs of vertices 1≤u<v≤n1≤𝑢<𝑣≤𝑛 between which there exists a path in this graph. To achieve this, you can remove exactly one edge from the graph.

Find the smallest number of pairs of vertices!

Input

Each test consists of several sets of input data. The first line contains a single integer t𝑡 (1≤t≤1041≤𝑡≤104) — the number of sets of input data. Then follows their description.

The first line of each set of input data contains two integers n𝑛 and m𝑚 (2≤n≤1052≤𝑛≤105, n−1≤m≤min(105,n⋅(n−1)2)𝑛−1≤𝑚≤min(105,𝑛⋅(𝑛−1)2)) — the number of vertices in the graph and the number of edges.

Each of the next m𝑚 lines contains two integers u𝑢 and v𝑣 (1≤u,v≤n,u≠v1≤𝑢,𝑣≤𝑛,𝑢≠𝑣), indicating that there is an undirected edge in the graph between vertices u𝑢 and v𝑣.

It is guaranteed that the given graph is connected and without multiple edges.

It is guaranteed that the sum of n𝑛 and the sum of m𝑚 over all sets of input data does not exceed 2e5.

基本思路:

1.求割边

我的理解是,割边是一个强连通分量如果去掉,则变成两个强连通分量的边,就是类似于两个岛屿直接唯一的桥。

其特点是low[son]<dfn[fa] 

son:子节点 fa:父节点

可以理解为如果经割边去到子节点,无法再从除父节点以外的地方回到父节点所在的连通分量

2.如何能够遍历割边(O(n)级别):tarjan算法

tarjan算法基本思路:

不同于tj需要利用栈去缩强连通分量,只需要在low[son]<dfn[fa],计算割边一侧的点数即可。

我们已知原图必然连通,所以只需要求从起始节点到割边处遍历过的节点的数量(包括这个fa节点)

3.最后踩过一些坑:vector等函数参数记得开引用,否则传参过程耗时直接上一个级别;记得开long long,虽然n在int 的范围之内,但是n*n不在。

#include<iostream>
#include<string.h>
#include<vector>
#include<map>
#include<algorithm>
#include<stack>
typedef long long ll;
using namespace std;

const ll MAXN=200010;
ll tot=0;
int dfn[MAXN]={},low[MAXN]={};
ll siz[MAXN]={};
ll ans=0;
ll n,m;
ll cal(ll cur)
{
	return (ll)((ll)siz[cur]*(ll)(siz[cur]-1)/(ll)2+((ll)(n-siz[cur])*(ll)(n-1-siz[cur])/(ll)2));
}
void tarjan(vector<vector<ll>>&to,ll cur,ll from){
	dfn[cur]=++tot;
	low[cur]=dfn[cur];
	siz[cur]=1;
	for(auto it:to[cur])
	{
		if(it==from)continue;
		if(!dfn[it]){
			tarjan(to,it,cur);
			siz[cur]+=siz[it];
		}
		if(low[it])low[cur]=min(low[cur],low[it]);
		if(dfn[cur]<low[it])
		{
			ans=min(ans,cal(it));
		}
	}
	
}
void slv(void)
{
	cin>>n>>m;
	tot=0;
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(siz,0,sizeof(siz));

	//while(stk.size()!=0)stk.pop();
	ans=(n*(n-1))/2;
	vector<vector<ll>>to(n+1);
	while(m--)
	{
		ll a,b;cin>>a>>b;
		to[a].push_back(b);
		to[b].push_back(a);
	}

	tarjan(to,1,-1);
	cout<<ans<<"\n";

}
int main(void)
{
	std::ios::sync_with_stdio(false);
	cin.tie(NULL);
	int t;cin>>t;
	for(int i=1;i<=t;i++)
	{
		slv();
	}
}

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值