题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4635
题目大意:
给定一个有向图,
问至多加多少条边,
使得新图是非强连通的简单图。
算法:
与其算加多少边,不如算有多少边不能加。
很容易想到,最终的图是分成两个强连通分支的。
这两个强联通分支内部分别是完全图,强连通分支之间的边都是由某个SCC指向另一个SCC的,不可能两个方向都有。
所以,不能加的就是两个SCC之间另一个方向上的边,数量就是两个SCC中点数的乘积。
不难看出让两个SCC的点数差距越大越好。
那么首先进行SCC缩点。
缩点后,如果一个SCC出度入度均不为0,那么它最终肯定是不可能单独作为剩下的两个SCC之一了,因为最后两个SCC间的边都是沿同一方向的。
在所有出度或入度为0的SCC中,我们找一个点数最少的。
那么最终就是这个SCC在一边,其它所有的点在另一边,中间的边都是沿着同一方向(出或入)。
代码如下:
#include <iostream> #include <cstring> #include <stdio.h> #include <math.h> #include <queue> #include <vector> #include <algorithm> #include <stack> #include <map> using namespace std; #define ll long long #define inf 2e9 #define pii pair<int,int> #define fr first #define sc second const int MAXN=110000; stack<int> ss; int dep[MAXN], low[MAXN], cot[MAXN]; int dex1[MAXN],dex2[MAXN],blk[MAXN]; vector<int> mm[MAXN]; void tarjan(int u, int p) { int tmp=low[u]=dep[u]=(p==-1)?0:dep[p]+1; ss.push(u); for(int i=0; i<mm[u].size(); i++) { int v=mm[u][i]; if(dep[v]==-1) { tarjan(v,u); } tmp=min(tmp,low[v]); } low[u]=tmp; if(low[u]==dep[u]) { while(ss.top()!=u) { cot[u]++; blk[ss.top()]=u; ss.pop(); } blk[ss.top()]=u; ss.pop(); cot[u]++; } } int main(){ int cas; scanf("%d", &cas); for(int T=1; T<=cas; T++) { int n, m; scanf("%d%d", &n, &m); memset(dep, -1, sizeof(dep)); memset(cot, 0, sizeof(cot)); memset(dex1, 0, sizeof(dex1)); memset(dex2, 0, sizeof(dex2)); while(!ss.empty()) { ss.pop(); } for(int i = 0; i < n; i ++) { mm[i].clear(); } for(int i = 0; i < m; i ++) { int u,v; scanf("%d%d", &u, &v); u--; v--; mm[u].push_back(v); } for(int i=0; i<n; i++) { if(dep[i]==-1) { tarjan(i,-1); } } for(int u=0; u<n; u++) { for(int i=0; i<mm[u].size(); i++) { int v=mm[u][i]; if(blk[u]==blk[v]) { continue; } dex1[blk[u]]++; dex2[blk[v]]++; } } long long ans=-1; for(int i=0; i<n; i++) { if(cot[i]==0||cot[i]==n) { continue; } if(dex1[i]&&dex2[i]) { continue; } ans=max(ans,(long long)n*(n-1)-(long long)cot[i]*(n-cot[i])-m); } printf("Case %d: %I64d\n",T,ans); } return 0; }