题意
T组测试数据,每组输入n,m表示n个点m条边,问最多增加多少条边可以使得图仍然不为强连通图。
思路
首先强连通缩点。
①找到入度为0或者出度为0且包含点最少的缩点,把该缩点内部补成完全图,假设该缩点内部含有
x
x
x个点,则边数为
x
∗
(
x
−
1
)
x*(x-1)
x∗(x−1)
②除了
x
x
x那个缩点包含的点以外其他点补成完全图,假设有
y
y
y个点,则边数为
y
∗
(
y
−
1
)
y*(y-1)
y∗(y−1)
③现在可以理解为只有
x
x
x和
y
y
y 两个点。如果
x
x
x 入度为0,那么我们把所有
x
x
x 中的点向
y
y
y 中的点建一条边。如果
x
x
x 出度为0,那么我们把所有
y
y
y 中的点向
x
x
x 中的点建一条边,这样保证了
x
x
x只有出度或者入度,
x
x
x和
y
y
y永远不可能强连通。则边数为
x
∗
y
x*y
x∗y
那么答案就是:
x
∗
(
x
−
1
)
+
y
∗
(
y
−
1
)
+
x
∗
y
−
m
x*(x-1) + y*(y-1) + x*y - m
x∗(x−1)+y∗(y−1)+x∗y−m
另一种理解方法:
我们把原图先搞成一个完全图,然后我们把上面那个①条件中的点的所有出边或者所有入边删掉。
那么答案就是:
n
∗
(
n
−
1
)
−
x
∗
y
−
m
n*(n-1) - x*y - m
n∗(n−1)−x∗y−m
注意答案开long long即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+5;
const int INF = 0x3f3f3f3f;
struct Edge
{
int from, to;
Edge (int from, int to): from(from), to(to) {}
};
struct SCC
{
int n, m;
int DFN[MAXN], LOW[MAXN], sccno[MAXN], dfs_clock, scc_cnt;
vector<Edge> edges;
vector<int> G[MAXN];
stack<int> S;
void init(int n)
{
this->n = n, m = 0;
edges.clear();
for (int i = 0; i <= n; i++) G[i].clear();
}
void AddEdge (int from, int to)
{
edges.push_back(Edge(from, to));
m = edges.size();
G[from].push_back(m-1);
}
void dfs(int u)
{
DFN[u] = LOW[u] = ++dfs_clock;
S.push(u);
for (int i = 0; i < G[u].size(); i++)
{
Edge e = edges[G[u][i]];
int v = e.to;
if (!DFN[v])
{
dfs(v);
LOW[u] = min(LOW[u], LOW[v]);
}
else if (!sccno[v])
LOW[u] = min(LOW[u], DFN[v]);
}
if (LOW[u] == DFN[u])
{
scc_cnt++;
while (1)
{
int x = S.top(); S.pop();
sccno[x] = scc_cnt;
if (x == u) break;
}
}
}
void find_scc()
{
dfs_clock = scc_cnt = 0;
memset(DFN, 0, sizeof(DFN)), memset(sccno, 0, sizeof(sccno));
for (int i = 0; i < n; i++)
if (!DFN[i]) dfs(i);
}
}gao;
int n, m, in[MAXN], out[MAXN], num[MAXN];
int main()
{
int T, CASE = 1; scanf("%d", &T);
while (T--)
{
scanf("%d%d", &n, &m);
gao.init(n);
for (int i = 0; i < m; i++)
{
int u, v; scanf("%d%d", &u, &v);
u--, v--;
gao.AddEdge(u, v);
}
gao.find_scc();
if (gao.scc_cnt == 1) { printf("Case %d: -1\n", CASE++); continue; }
memset(num, 0, sizeof(num));
for (int i = 0; i < n; i++) num[gao.sccno[i]]++;
memset(in, 0, sizeof(in)), memset(out, 0, sizeof(out));
for (int i = 0; i < gao.m; i++)
{
int u = gao.edges[i].from, v = gao.edges[i].to;
if (gao.sccno[u] != gao.sccno[v])
out[gao.sccno[u]]++, in[gao.sccno[v]]++;
}
int x = INF;
for (int i = 1; i <= gao.scc_cnt; i++)
if (in[i] == 0 || out[i] == 0) x = min(x, num[i]);
int y = n-x;
long long ans = 1LL*x*(x-1) + 1LL*y*(y-1) + 1LL*x*y - m;
//long long ans = 1LL*n*(n-1) - 1LL*x*y - m;
printf("Case %d: %lld\n", CASE++, ans);
}
return 0;
}
/*
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
*/