hdu 4635 非强连通图最大边数

第四场多校1004题,结果可能超 int,需要理解强连通缩点后图的机构

比赛的时候没有搞出来,看来自己不是能力不够,我还不够谈定

题意:

让一个图作为一个简单图,最多能有几条边。

分析:

简单图:没有自环(输入中没有)、没有重边(输入中没有)、不是强连通图,输入保证为简单图,在已有的边上,我们只要分析

得出这种连通情况下总共能加多少边,然后减去已有的边的个数即可。

答案:

ans = (n - x)*(n - x - 1) + x*(x - 1) + x*(n - x); x为某一连通

图内所含节点个数。

解析:

当图变成两个强连通分量的图时,边的个数最大。

    证明:假设图有 m(m > 2) 个强连通分量时边数最多,如果将其中的两个强连通分量用边构成一个强连通分量,那就一定要至少加一条边使得两个分量合并为一个分量,则当图中有 m(m > 2) 个强连通分量时,边数一定不是最多。

将 ans 求导,发现:x = n / 2 时,ans 最小,显然 1 <= x < x - 1; 则 x 越小越好。

记得只有到某一强连通块在重建的图中出度或者入读为 0 时,才能被选为 x。为什么呢?想想缩点后连通图结构吧。

//2013-08-01 22:43:44	Accepted	4635	62MS	4416K	2054 B	C++	vampire_rui
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef __int64 lld;
const int maxn = 110000;
const lld inf = 200000000000;
vector<int>edge[maxn];
int n, r, scc[maxn], in[maxn], out[maxn];
int tmpdfn, dfn[maxn], low[maxn], inst[maxn], belong[maxn], st[maxn], top, scnt;
void tarjan( int u )
{
	int i, v, t, size;
	low[u] = dfn[u] = tmpdfn++;
	st[top++] = u;
	inst[u] = 1;
	size = edge[u].size();
	for(i = 0; i < size; i++)
	{
		v = edge[u][i];
		if(dfn[v] == -1)
		{
			tarjan( v );
			low[u] = min( low[u], low[v] );
		}
		else if(inst[v])low[u] = min( low[u], dfn[v] );
	}
	if(dfn[u] == low[u])
	{
		do{ belong[t = st[--top]] = scnt; inst[t] = 0; }while( t != u );
		scnt++;
	}
}
void rebuild()
{
	int i, u, v, size;
	memset(in, 0, sizeof(in));
	memset(out, 0, sizeof(out));
	for(u = 1; u <= n; u++)
	{
		size = edge[u].size();
		for(i = 0; i < size; i++)
		{
			v = edge[u][i];
			if(belong[u] != belong[v])
			{
				in[belong[v]]++;
				out[belong[u]]++;
			}
		}
	}
}
bool SCC()
{
	int i;
	top = 0;
	tmpdfn = scnt = 1;
	memset(dfn, -1, sizeof(dfn));
	memset(inst, 0, sizeof(inst));
	for(i = 1; i <= n; i++)if(dfn[i] == -1)tarjan( i );
	if(scnt <= 2)return false;
	memset(scc, 0, sizeof(scc));
	for(i = 1; i <= n; i++)scc[belong[i]]++;
	rebuild();
	return true;
}
int main()
{
	int T, cas, u, v, i, j;
	lld ans;
	scanf( "%d", &T );
	for(cas = 1; cas <= T; cas++)
	{
		scanf( "%d%d", &n, &r );
		for(i = 1; i <= n; i++)edge[i].clear();
		for(i = 1; i <= r; i++)
		{
			scanf( "%d%d", &u, &v );
			edge[u].push_back( v );
		}
		bool flag = SCC();
		printf( "Case %d: ", cas );
		if(flag)
		{
			int mins = 100000000;
			for(i = 1; i < scnt; i++)if(!in[i] || !out[i])
				if(scc[i] < mins)mins = scc[i];
			lld minS = (lld)mins;
			lld N = (lld)n;
			ans = minS * (minS - 1) + (N - minS) * (N - minS - 1) + minS * (N - minS);
			ans -= (lld)r;
			printf( "%I64d\n", ans );
		}
		else puts("-1");
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值