1 /*
2 题意:给出简单有向图的定义:不能有自环,没有重边,不能强连通;
3 问给出一个初始图,最多加多少条边可以使得这个图满足上述定义。
4
5 题解:强连通缩点
6 首先没有自环,没有重边都十分清晰,然后不能强连通给了一个提示,就是要找出连通分量,那么怎样才能加最多的边
7 而使得这个图不是强连通,只需保证将这个图分成两个点集,且其中的一个点集到另一点集只能含有单向的边,然后这
8 两个点集之内的点是强连通,就保证了最优情况的非强连通,由于有一些边是已存在,因此对于加的边的数目来说要最
9 优的话,必须找出加边最多的情况;然后需要推出加边的公式:
10 ans = N - m - a * b
11 其中N是指整个图为竞赛图时的总边数,然后已经存在的边为m需要减去,然后就是找出a*b,a,b是指将图分成两个点集
12 之后两个点集的点的数量,理由:分成两个点集之后,一个点集到另一个点集需要的只有单向边,因此要将所有的反向
13 边都删除,因为是竞赛图,因此所要去除的边的数目即为a*b,减去之后求出的解就是加边数目,而要使得ans最小,则
14 a*b最小即可;通过强连通缩点将不可能的点集合成一个点,因为从这些点挑选出的部分点集必然会导致分出的两个点集
15 有回路,因此强连通分量需要选取所有的点,然后得出的一个缩点后的图就是一个拓扑图,我们要选取的应该是a尽量小
16 的点集,b=(n-a),那么选一个包含点数目最小的点集就是好了,因此应该选择出度或入度为0的点集,因为如果选择的
17 是出入度均不为0的点集作为a点集,由于有已存在的边不能删除则必然是存在边返回剩余点集,那样不符合要求,因此
18 需要选择出入度为0的最小点集。
19 */
20 #include <cstdio>
21 #include <cstring>
22
23 #define clr(a,b) (memset(a,b,sizeof(a)))
24 #define cpy(a,b) (memcpy(a,b,sizeof(a)))
25 const int NV = 100005;
26 const int NE = 100005;
27
28 inline int Min(int a, int b) {return a < b ? a : b;}
29 inline int Max(int a, int b) {return a > b ? a : b;}
30 int deep, scc, top, SZ, n;
31 struct edge
32 {int v, next;} E[NE];
33 int head[NV], dfn[NV], low[NV], id[NV], st[NV];
34 int indegree[NV],outdegree[NV];
35 int mindegree[NV],moutdegree[NV];
36 int sum[NV];
37 bool in[NV];
38
39 inline void init(int _n) {
40 n = _n;
41 clr(head, -1);
42 clr(dfn, -1);
43 clr(in,false);
44 deep = scc = SZ = top = 0;
45 }
46 void tarjan(int u) {
47 int v, i;
48 dfn[u] = low[u] = ++ deep;
49 st[top++] = u;
50 in[u] = true;
51 for (i = head[u]; i != -1; i = E[i].next) {
52 v = E[i].v;
53 if (dfn[v] == -1) {
54 tarjan(v);
55 low[u] = Min(low[u], low[v]);
56 }
57 else if (in[v]) {
58 low[u] = Min(low[u], dfn[v]);
59 }
60 }
61 if (low[u] == dfn[u]) {
62 int tsum = 0;
63 do {
64 tsum ++;
65 v = st[--top];
66 in[v] = false;
67 id[v] = scc; // 缩点
68 } while (u != v);
69 sum[scc] = tsum;
70 scc ++;
71 }
72 }
73 inline void insert(int u, int v) {
74 E[SZ].v = v;
75 E[SZ].next = head[u];
76 head[u] = SZ ++;
77 }
78 inline void solve() {
79 for (int i = 1; i <= n; i++) {
80 if (dfn[i] == -1) {
81 tarjan(i);
82 }
83 }
84 }
85
86 int main(void)
87 {
88 int t,n,m;
89 scanf("%d",&t);
90 for(int cas = 1; cas <= t; cas++)
91 {
92 scanf("%d%d",&n,&m);
93 init(n);
94 clr(indegree,0);
95 clr(outdegree,0);
96 clr(sum,0);
97 for(int i=0; i<m; i++)
98 {
99 int u,v;
100 scanf("%d%d",&u,&v);
101 insert(u,v);
102 }
103 solve();
104 if (scc == 1)
105 {
106 printf("Case %d: -1\n",cas);
107 continue;
108 }
109
110 for(int i=1; i<=n; i++)
111 {
112 for (int j = head[i]; j != -1; j = E[j].next)
113 {
114 if (id[i] != id[E[j].v])
115 outdegree[id[i]]++,indegree[id[E[j].v]]++;
116 }
117 }
118
119 int tans = n*n;
120 for(int i=0; i<scc; i++)
121 if (indegree[i] == 0 || outdegree[i] == 0)
122 tans = Min(tans, sum[i]*(n-sum[i]));
123 printf("Case %d: %d\n",cas,n*(n-1)-m-tans);
124 }
125 return 0;
126 }