链接:http://acm.hdu.edu.cn/showproblem.php?pid=4635
大意:给出一个简单有向图,问最多可以向图中添加几条边,并且保证图不是强连通
思路:
1.原图至多加几条边形成一个非强联通图 <=>(等价于) 一个完全图至少删几条边形成一个非强联通图(不删原图
边);
2.原图经过加边、缩点后会形成一个x点集 到 y点集的最终图形;x --> y
3.相比完全图来说删去了y点集中的每个点指向x点集中的每个点的边,数量为x * y;
4.为了使删的边尽量少,已知x + y = const ,则x 或 y 越小,x * y 越小;
5.易得x , y至少包含原图缩点后 那些入度为0 或 出度为 0 的点集 ai;
6.找到最小的ai为minn;
7.ans = n * (n - 1) - m - minn * (n - minn)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
#define MAXN 100010
vector<int>vec[MAXN];
int dfn[MAXN],id[MAXN],stack[MAXN],num[MAXN];
bool in[MAXN],out[MAXN];
int scc,cnt,top;
int tarjan(int u)
{
int lowu = dfn[u] = ++cnt;
stack[++top] = u;
for(int i = 0;i < vec[u].size();i++)
{
int v = vec[u][i];
if(!dfn[v])
{
int lowv = tarjan(v);
lowu = min(lowu,lowv);
}
else if(!id[v])
lowu = min(lowu,dfn[v]);
}
if(lowu == dfn[u])
{
scc++;
num[scc] = 0;
do
{
id[stack[top--]] = scc;
num[scc]++;
}while(stack[top + 1] != u);
}
return lowu;
}
long long solve(int n,int m)
{
memset(dfn,0,sizeof(dfn));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(id,0,sizeof(id));
scc = top = cnt = 0;
for(int i = 1;i <= n;i++)
if(!dfn[i])tarjan(i);
if(scc == 1)return -1;
for(int u = 1;u <= n;u++)
for(int i = 0;i < vec[u].size();i++)
if(id[u] != id[vec[u][i]])
in[id[vec[u][i]]] = out[id[u]] = true;
long long minn = n;
for(int i = 1;i <= scc;i++)
if(!out[i] || !in[i])minn = min(minn,(long long)num[i]);
return (long long)n * (n - 1) - m - minn * (n - minn);
}
int main()
{
int _,n,m,u,v;
scanf("%d",&_);
for(int kcas = 1;kcas <= _;kcas++)
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)vec[i].clear();
for(int i = 1;i <= m;i++)
{
scanf("%d%d",&u,&v);
vec[u].push_back(v);
}
long long ans = solve(n,m);
if(ans == -1)
printf("Case %d: -1\n",kcas);
else
printf("Case %d: %I64d\n",kcas,ans);
}
}