第四场多校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;
}