题目链接:https://vjudge.net/problem/HDU-4635
题目:有向图,给定若干个连通图,求最多还能添加几条边,添完边后,图仍然要满足
(1)是简单图,即没有重边或者自环
(2)不是有向强连通图
思路:我们可以这么想,n个顶点,一个有向图边数最多,就是有向完全图,则边数为n*(n-1)。
要满足不是强连通图,我们可以假设有一个tarjan缩成的点(scc),它不能到达其他所有点,或者其他所有点
不能到达它,假设这个scc有k个顶点,也就是说,k*(n-k)条边是不存在的,那么最大添加边数应该是 n*(n-1) - k*(n-k) - m(本来有的边) ---- ①
给定的图情况不确定,我们先进行tarjan缩点,分成若干个scc,每个scc中也记录各自scc中的顶点数,然后进行scc的入度出度统计。
为使添加的边能最多,我们选所有的入度为0或者出度为0的scc 套入 公式①,选最大即可。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 6 typedef long long ll; 7 const int N = (int)1e5+10; 8 int n,m,tot,tim,scc,top; 9 int head[N],dfn[N],low[N],scc_no[N],s[N],ins[N]; 10 struct node{ 11 int to; 12 int nxt; 13 }e[N]; 14 struct col{ 15 int ru; 16 int chu; 17 int cnt; 18 }col[N];//强连通分量 19 20 void init(){ 21 for(int i = 0; i <= n; ++i){ 22 head[i] = -1; 23 col[i].ru = col[i].chu = dfn[i] = 0; 24 } 25 tot = tim = scc = 0; 26 } 27 28 inline void add(int u,int v){ 29 e[tot].to = v; 30 e[tot].nxt = head[u]; 31 head[u] = tot++; 32 } 33 34 void tarjan(int now,int pre){ 35 dfn[now] = low[now] = ++tim; 36 ins[now] = 1; s[top++] = now; 37 int to; 38 for(int o = head[now]; ~o; o = e[o].nxt){ 39 to = e[o].to; 40 // if(to == pre) continue; 41 if(!dfn[to]){ 42 tarjan(to,now); 43 low[now] = min(low[now],low[to]); 44 } 45 else if(ins[to]) low[now] = min(low[now],dfn[to]); 46 } 47 48 if(dfn[now] == low[now]){ 49 int x,cnt = 0; 50 ++scc; 51 do{ 52 x = s[--top]; 53 ins[x] = 0; 54 scc_no[x] = scc; 55 ++cnt; 56 }while(now != x); 57 col[scc].cnt = cnt;//每个强连通分量包含几个点 58 } 59 } 60 61 //入度出度统计 62 void du_cnt(){ 63 int to; 64 for(int now = 1; now <= n; ++now){ 65 for(int o = head[now]; ~o; o = e[o].nxt){ 66 to = e[o].to; 67 if(scc_no[to] == scc_no[now]) continue; 68 ++col[scc_no[to]].ru; 69 ++col[scc_no[now]].chu; 70 } 71 } 72 } 73 74 void solve(int _case){ 75 for(int i = 1; i <= n; ++i) 76 if(!dfn[i]) tarjan(i,i); 77 78 ll ans = (ll)n*(n-1) - m; 79 ll useless = (1LL) << 60; 80 du_cnt(); 81 for(int i = 1; i <= scc; ++i){ 82 if(!col[i].ru || !col[i].chu){ 83 useless = min(useless,(ll)col[i].cnt*(n-col[i].cnt)); 84 } 85 } 86 if(scc == 1) printf("Case %d: -1\n",_case); 87 else printf("Case %d: %lld\n",_case,ans-useless); 88 } 89 90 int main(){ 91 92 int T,u,v; 93 scanf("%d",&T); 94 for(int i = 1; i <= T; ++i){ 95 scanf("%d%d",&n,&m); 96 init(); 97 for(int x = 1; x <= m; ++x){ 98 scanf("%d%d",&u,&v); 99 add(u,v); 100 } 101 solve(i); 102 } 103 104 return 0; 105 }