`题目:
http://acm.hdu.edu.cn/showproblem.php?pid=4635
`题意:
给你一个图, 若为强连通图,则输出-1.
否则求出至多加上多少边使图为非强连通图.
`思路:
从正面加边无法进行, 则从反面想, 一个非强连通图最多有多少边, 再减去已存在的边.
若强连通分量为1, 则输出-1.
否则, 强连通缩点, 将点分成两部分,X和Y. X和Y之间只能有一个方向指向, 而X或Y内部则可以互相指向.
枚举入度或者出度为0的点,从而得到的最大的答案.
AC.
#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int n, m;
int in[maxn], out[maxn];
vector<int> G[maxn];
int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt;
int cnt[maxn];
stack<int> S;
void dfs(int u)
{
pre[u] = lowlink[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);
lowlink[u] = min(lowlink[u], lowlink[v]);
}
else if(!sccno[v]) {
lowlink[u] = min(lowlink[u], pre[v]);
}
}
if(lowlink[u] == pre[u]) {
scc_cnt++;
while(1) {
int x = S.top(); S.pop();
sccno[x] = scc_cnt;
cnt[scc_cnt]++;
if(x == u) break;
}
}
}
void find_scc()
{
dfs_clock = scc_cnt = 0;
memset(sccno, 0, sizeof(sccno));
memset(pre, 0, sizeof(pre));
memset(cnt, 0, sizeof(cnt));
for(int i = 0; i < n; ++i) {
if(!pre[i]) dfs(i);
}
}
int main()
{
//freopen("in", "r", stdin);
int T;
scanf("%d", &T);
int ca = 1;
while(T--) {
scanf("%d %d", &n, &m);
for(int i = 0; i < n; ++i)
G[i].clear();
for(int i = 0; i < m; ++i) {
int u, v;
scanf("%d %d", &u, &v);
u--; v--;
G[u].push_back(v);
}
find_scc();
printf("Case %d: ", ca++);
if(scc_cnt == 1) {
printf("-1\n");
}
else if(scc_cnt > 1) {
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
for(int i = 0; i < n; ++i) {
for(int j = 0; j < G[i].size(); ++j) {
int v = G[i][j];
if(sccno[v] == sccno[i]) continue;
in[sccno[v]]++;
out[sccno[i]]++;
}
}
ll res = 0;
for(int i = 1; i <= scc_cnt; ++i) {
if(in[i] == 0 || out[i] == 0) {
res = max(res, (ll)cnt[i]*(n-cnt[i]) + //X与Y之间的边数
(ll)(n-cnt[i])*(n-cnt[i]-1) + //Y 内的边数
(ll)cnt[i]*(cnt[i]-1)); //X内的边数
//printf("%lld\n", res);
}
}
//printf("%lld\n", res);
printf("%I64d\n", res - m);
}
}
return 0;
}