【HDU - 4635】Strongly connected(缩点,新图性质,建图,Tarjan求强连通分量)

版权声明:欢迎学习我的博客,希望ACM的发展越来越好~ https://blog.csdn.net/qq_41289920/article/details/88820755

题干:

Give a simple directed graph with N nodes and M edges. Please tell me the maximum number of the edges you can add that the graph is still a simple directed graph. Also, after you add these edges, this graph must NOT be strongly connected. 
A simple directed graph is a directed graph having no multiple edges or graph loops.
A strongly connected digraph is a directed graph in which it is possible to reach any node starting from any other node by traversing edges in the direction(s) in which they point. 

Input

The first line of date is an integer T, which is the number of the text cases. 
Then T cases follow, each case starts of two numbers N and M, 1<=N<=100000, 1<=M<=100000, representing the number of nodes and the number of edges, then M lines follow. Each line contains two integers x and y, means that there is a edge from x to y.

Output

For each case, you should output the maximum number of the edges you can add. 
If the original graph is strongly connected, just output -1.

Sample Input

3
3 3
1 2
2 3
3 1
3 3
1 2
2 3
1 3
6 6
1 2
2 3
3 1
4 5
5 6
6 4

Sample Output

Case 1: -1
Case 2: 1
Case 3: 15

解题报告:

    这题首先缩点,因为这些点之间肯定是强连通的,我们为了不让他是个强连通图,就是要新图不再是一个强连通分量就可以了。Tarjan处理完了之后得到每个集合的点数和一张新图,我们不去建图,只需要记录入度和出度,这样就得到了一张DAG图。我们要想加的边尽量多但是不让他是一个强连通图,那么就需要让这个新图的scc控制在2。也就是刚刚好不是个强连通,所以我们先假设连上了所有的边,然后考虑哪些边是不能加的。特就是将新图分成两个大集合,让他们之间只有单向边就可以了。也就是我们要找到一个大集合A和大集合B,元素个数假设分别为x和n-x,这样要减去的边数就是x*(n-x),要让这个值尽量小,所以我们枚举每一个小集合(要出度为0或者入度为0的小集合),求出以他为A大集合,需要减去的边数,维护最小值就可以了。

至于为什么要出度为0或者入度为0的小集合呢?因为如果是中间的某个集合,那么也要去掉的是  与当前DAG序列的所有入度为零的小集合 之间的那些连边,所以那样肯定减去的很多,而且可以直接归属在我们写的这种算法中。

扫描二维码关注公众号,回复: 5799753 查看本文章

当然也可以不用减法,直接A*B + A*(A-1) + B*(B-1) - m

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
struct Edge {
	int to;
	int ne;
} e[MAX];
int head[MAX],tot;
int DFN[MAX],LOW[MAX],col[MAX],clk,index,scc,stk[MAX],vis[MAX];
ll cnt[MAX];
int n,m;
int in[MAX],out[MAX];
void add(int u,int v) {
	e[++tot].to = v;
	e[tot].ne = head[u];
	head[u] = tot;
}
void Tarjan(int x) {
	DFN[x] = LOW[x] = ++clk;
	vis[x] = 1;
	stk[++index] = x;
	for(int i = head[x]; ~i; i = e[i].ne) {
		int v = e[i].to;
		if(!DFN[v]) {
			Tarjan(v);
			LOW[x] = min(LOW[x],LOW[v]);
		}
		else if(vis[v]) LOW[x] = min(LOW[x],DFN[v]);
	}
	if(LOW[x] == DFN[x]) {
		scc++;
		while(1) {
			int tmp = stk[index];index--;
			vis[tmp] = 0;
			col[tmp] = scc;
			cnt[scc]++;
			if(tmp == x) break;
		}
	}
} 
void init() {
	memset(head,-1,sizeof head);
	tot = 0;
	for(int i = 1; i<=n; i++) {
		DFN[i] = LOW[i] = cnt[i] = 0;
		in[i] = out[i] = 0;
	}
	clk = 0;index = 0;scc = 0;
}
int main()
{
	int t,iCase=0;
	cin>>t;
	while(t--) {
		scanf("%d%d",&n,&m);
		init();
		for(int a,b,i = 1; i<=m; i++) {
			scanf("%d%d",&a,&b);
			add(a,b);
		}
		for(int i = 1; i<=n; i++) {
			if(!DFN[i]) Tarjan(i);
		}
		if(scc == 1) {
			printf("Case %d: -1\n",++iCase);continue;
		}
		for(int u = 1; u<=n; u++) {
			for(int i = head[u]; ~i; i = e[i].ne) {
				int v = e[i].to;
				if(col[u] != col[v]) {
					out[col[u]]++;
					in[col[v]]++;
				}
			}
		}
		ll minn = n;
		for(int i = 1; i<=scc; i++) {
			if(in[i] == 0 || out[i] == 0) {
				minn = min(minn,cnt[i]);
			}
		} 
		ll ans = 1LL * n * (n-1) - m;//最多还能增加这些边 
		printf("Case %d: %lld\n",++iCase,ans - minn * (n-minn) );
	}


	return 0 ;
}
/*
14:36 - 14:53
*/

猜你喜欢

转载自blog.csdn.net/qq_41289920/article/details/88820755