hdu 4635 Tarjan缩点 + 添边

题意:

n个点m条边的有向图,在保证不是强连通图的情况下最多可以添加多少条边。1 <= n <= 1e5 ,1 <= m <= 1e5。

题解:

1.正向思考不容易想到,逆向思考,把该有向图变成完全图,即n*(n-1)条边,在不减少原有的m条边内的边的条件下最多可以减少多少条边。最后得到一个有两个强连通分量的图,记作X和Y,X分量有x个点,Y分量有y个点,X分量是完全子图,Y分量是完全子图,X分量的每个点都有指向Y分量的每个点的边。可添加x*(x-1) + y*(y-1) + x*y - m = n*(n-1) - x * y - m条边。尽量使x * y小,即尽量使abs(x - y)大。

2.Tarjan缩点,记录每个强连通分量的点的个数。然后找到最小的分量点数num,答案为 n*(n-1) - num * (n - num) - m

#include<bits/stdc++.h>
#define N 100005
#define inf 0x3f3f3f3f
using namespace std ;
struct Edge
{
	int to , next ;
} edge[N] ;
int head[N] ;
int vis[N] , dfn[N] , low[N] , s[N] ;
int id[N] , in[N] , out[N] ;
long long scc[N] ;
int scc_num , bridge_num ;
int top1 , lay , cnt ;
long long n , m ;
void init()
{
	top1 = lay = 0 ;
	scc_num = bridge_num = 0 ;
	cnt = 0 ;
    memset(head , -1 , sizeof(head)) ;
    memset(vis , 0 , sizeof(vis)) ;
    memset(in , 0 , sizeof(in)) ;
    memset(out , 0 , sizeof(out)) ;
    memset(scc , 0 , sizeof(scc)) ;
}
void addedge(int u , int v)
{
	edge[cnt].to = v ;
	edge[cnt].next = head[u] ;
	head[u] = cnt ++ ;
}
void Tarjan(int u)
{
	int v ;
	int i , j ;
	lay ++ ;
	vis[u] = 1 ;
	low[u] = lay ;
    dfn[u] = lay ;
    s[++ top1] = u ;
	for(i = head[u] ; i != -1 ; i = edge[i].next)
    {
    	v = edge[i].to ;
    	if(vis[v] == 1)
    	   low[u] = min(low[u] , dfn[v]) ;
    	if(vis[v] == 0)
    	{
    		Tarjan(v) ;
    		low[u] = min(low[u] , low[v]) ;
		}
	}
	if(dfn[u] == low[u])
    {
    	scc_num ++ ;
    	while(s[top1] != u)
    	{
    		scc[scc_num] ++ ;
    		id[s[top1]] = scc_num ;
    		vis[s[top1]] = 2 ;
    		top1 -- ;
		}
		scc[scc_num] ++ ;
		id[s[top1]] = scc_num ;
		vis[s[top1]] = 2 ;
    	top1 -- ;
	}
}
void find()
{
	int i , j ;
	for(i = 1 ; i <= n ; i ++)
	   for(j = head[i] ; j != -1 ; j = edge[j].next)
	   {
	   	 if(id[i] != id[edge[j].to])
	   	 {
	   	   out[id[i]] ++ ;
		   in[id[edge[j].to]] ++ ; 	
		 }
	   }
}
long long cal()
{
	int i ;
	long long x , y ;
	long long num = n ;
	long long ans = 0 ;
	for(i = 1 ; i <= scc_num ; i ++)
	{
	   if(in[i] != 0 && out[i] != 0)
	      continue ;
	   num = min(num , scc[i]) ;	
	}
	ans = n * (n - 1) - num * (n - num) - m ;
	return ans ;
}
int main()
{
   	int t , case_num = 0 ;
   	int i , j ;
   	int u , v ;
   	long long ans ;
   	scanf("%d" , &t) ;
	while(t --)
	{
		init() ;
		scanf("%lld%lld" , &n , &m) ;
		for(i = 1 ; i <= m ; i ++)
		{
			scanf("%d%d" , &u , &v) ;
			addedge(u , v) ;
		}
		for(i = 1 ; i <= n ; i ++)
		   if(vis[i] == 0)
		     Tarjan(i) ; //缩点
		find() ; // 找入度为0的点和出度为0的点 
		ans = cal() ; // 计算答案 
		if(scc_num == 1)
		   ans = -1 ;
		printf("Case %d: %lld\n" , ++case_num , ans) ;
	}
}

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/88092234
今日推荐