想法一:
找出强联通块,计算每个连通块内的点数。将点数最少的那个连通块单独拿出来,其余的连通块合并成一个连通分量。 那么假设第一个连通块的 点数是 x 第二个连通块的点数是 y
一个【强】连通图最多(每两个点之间,至少存在一条课互相到达的路径)的边数为n*(n-1)
一个连通图的边数至少为n*(n-1)- x*y + 1
则非连通图最多的边数为n*(n-1)- x*y 即 x*(x-1)+ y*(y-1)+ x*y
因为原图中已经有m条边 所以最多加 x*(x-1)+ y*(y-1)+ x*y - m 条边
这里最少点数的强联通分量要满足一个条件,就是出度或者入度为 0才行,不然是不满足的。
二:
缩点后
这其实就相当于一个完全图至少减去多少条边,使之变成非强连通图
肯定减去连通分量里点最少的那个了
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #define mem(a, b) memset(a, b, sizeof(a)) using namespace std; const int maxn = 300100, INF = 0x7fffffff; vector<int> G[maxn]; int pre[maxn], low[maxn], cnt[maxn], dfs_clock, scc_cnt, sccno[maxn]; int in[maxn], out[maxn]; stack<int> S; int n, m; void dfs(int u) { pre[u] = low[u] = ++dfs_clock; S.push(u); for(int i=0; i<G[u].size(); i++) { int v = G[u][i]; if(!pre[v]) { dfs(v); low[u] = min(low[u], low[v]); } else if(!sccno[v]) { low[u] = min(low[u], pre[v]); } } if(low[u] == pre[u]) { scc_cnt++; for(;;) { int x = S.top(); S.pop(); sccno[x] = scc_cnt; //标记x属于哪一个强连通块 cnt[scc_cnt]++; //统计当前强连通块中元素的个数 if(x == u) break; } } } void init() { mem(cnt, 0); mem(pre, 0); mem(in, 0); mem(out, 0); mem(low, 0); mem(sccno, 0); for(int i=0; i<=n; i++) G[i].clear(); dfs_clock = 0; scc_cnt = 0; } int main() { int T, kase = 0; cin>> T; while(T--) { cin>> n >> m; init(); for(int i=0; i<m; i++) { int u, v; cin>> u >> v; G[u].push_back(v); } for(int i=1; i<=n; i++) if(!pre[i]) dfs(i); int minx = INF; for(int i=1; i<=n; i++) for(int j=0; j<G[i].size(); j++) if(sccno[i] != sccno[G[i][j]]) out[sccno[i]]++, in[sccno[G[i][j]]]++; for(int i=1; i<=scc_cnt; i++) if(in[i] == 0 || out[i] == 0) minx = min(minx, cnt[i]); // cout<< minx <<endl; printf("Case %d: ",++kase); if(scc_cnt == 1) cout<< "-1" <<endl; else cout<< n*(n-1) - minx*(n-minx) - m <<endl; } return 0; }