1. 程式人生 > >hdu 4635 Strongly connected (Tarjan)

hdu 4635 Strongly connected (Tarjan)

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=4635

Problem Description

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

 

題意:給一個n個點的簡單有向圖,問最多能加多少條邊使得該圖仍然是簡單有向圖,且不是強連通圖。簡單有向圖定義:沒有重邊,無自環。 強連通圖:整個圖縮點後就只有一個點,裡面包含n個原點,也就是一個連通分量。如果一開始就是一個強連通圖,則輸出-1。
解題:要加邊最多那麼加邊後的圖連通分量越少越好,那麼連通分量最少也就是2個。先用n個點構造一個完全圖(有向圖有:n*(n-1)條邊,無向圖有:n*(n-1)/2條邊),再用構造的邊 減去原來有的m條邊=ans。再用強連通演算法縮點,記錄每個新點包含點的個數,從入度為0或出度為0的新點中找出包含點數最小的minnum,再用上面剩餘的邊ans - minnum*(n-minnum)就是所要的答案。因為如果不減入度為0或出度為0相關的邊,那麼該點本身包含有入邊和出邊,加的邊永遠都是強連通圖。所以只能去掉與入度為0或出度為0點的相關邊,只減掉一個方向的邊,要麼全是(n-minnum)點數到minnum點數的入邊,那麼是minnum點數到(n-minnum)點數的出邊。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll __int64
const int N = 100005;
struct EDG 
{
	int to, next;
}edg[N];
int eid, head[N];
int low[N], dfn[N], instack[N], num[N], belong[N], deep, stack1[N], tn, top;
int in[N], out[N];

void init()
{
	eid = tn = top = deep = 0;
	memset(head, -1, sizeof(head));
	memset(in, 0, sizeof(in));
	memset(out, 0, sizeof(out));
	memset(num, 0, sizeof(num));
	memset(dfn, -1, sizeof(dfn));
	memset(low, 0, sizeof(low));
	memset(instack, 0, sizeof(instack));
	memset(stack1, 0, sizeof(stack1));
	return;
}
void addEdg(int u, int v) 
{
	edg[eid].to = v; edg[eid].next = head[u]; head[u] = eid++;
}
void tarjer(int u)
{
	stack1[top++] = u;
	deep++;
	low[u] = dfn[u] = deep;
	instack[u] = 1;
	for (int i = head[u]; i != -1; i = edg[i].next)
	{
		int v = edg[i].to;
		if (dfn[v]==-1)
		{
			tarjer(v);
			low[u] = min(low[u], low[v]);
		}
		else if (instack[v] == 1)
			low[u] = min(low[u], dfn[v]);
	}
	if (low[u] == dfn[u])
	{
		tn++;
		int v;
		do 
		{
			v = stack1[--top];
			num[tn]++;      //記錄縮點后里面原來點的個數
			instack[v] = 0;
			belong[v] = tn;
		} while (v != u);

	}
}
ll solve(int n, int m)
{
	ll ans = n * (n - 1) - m;
	int minnum = N;
	for (int i = 1; i <= n; i++)
	{
		if (dfn[i] == -1)
		{
			tarjer(i);
		}
	}
	if (tn == 1) return -1;
	for (int u = 1; u <= n; u++)
		for (int i = head[u]; i != -1; i = edg[i].next) 
		{
			int v = edg[i].to;
			if (belong[u] != belong[v])
			{
				in[belong[v]]++, out[belong[u]]++;
			}
		}
	for (int i = 1; i <= tn; i++)
		if (in[i] == 0 || out[i] == 0) {
			minnum = min(minnum, num[i]);
		}
	ans -= minnum * (n - minnum);

	return ans;
}
int main()
{
	//freopen("C://input.txt", "r", stdin);
	int T, n, m, c = 0, a, b;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d", &n, &m);
		init();
		for (int i = 1; i <= m; i++)
		{
			scanf("%d%d", &a, &b);
			addEdg(a, b);
		}
		printf("Case %d: %I64d\n", ++c, solve(n, m));
	}
}