HDU 4635 Strongly connected (强连通分量)

题意

给定一个N个点M条边的简单图,求最多能加几条边,使得这个图仍然不是一个强连通图。

思路

2013多校第四场1004题。和官方题解思路一样,就直接贴了~ 最终添加完边的图,肯定可以分成两个部X和Y,其中只有X到Y的边没有Y到X的边,那么要使得边数尽可能的多,则X部肯定是一个完全图,Y部也是,同时X部中每个点到Y部的每个点都有一条边,假设X部有x个点,Y部有y个点,有x+y=n,同时边数F=x*y+x*(x-1)+y*(y-1),整理得:F=N*N-N-x*y,当x+y为定值时,二者越接近,x*y越大,所以要使得边数最多,那么X部和Y部的点数的个数差距就要越大,所以首先对于给定的有向图缩点,对于缩点后的每个点,如果它的出度或者入度为0,那么它才有可能成为X部或者Y部,所以只要求缩点之后的出度或者入度为0的点中,包含节点数最少的那个点,令它为一个部,其它所有点加起来做另一个部,就可以得到最多边数的图了

代码

 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MID(x,y) ((x+y)/2)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN = 10005;
const int MAXM = 50005;
struct SCC{
    int scc_num, scc[MAXN];         //强连通分量总数、每个节点所属的强连通分量
    int scc_acount[MAXN];           //每个强连通分量的节点个数
    int dfn[MAXN], low[MAXN], id;
    stack  st;
    bool vis[MAXN], instack[MAXN];
    int cnt, head[MAXN];

    struct node{
        int u, v;
        int next;
    }arc[MAXM];

    void init(){
        cnt = 0;
        mem(head, -1);
        return ;
    }
    void add(int u, int v){
        arc[cnt].u = u;
        arc[cnt].v = v;
        arc[cnt].next = head[u];
        head[u] = cnt ++;
    }
    void dfs(int u){
        vis[u] = instack[u] = 1;
        st.push(u);
        dfn[u] = low[u] = ++ id;
        for (int i = head[u]; i != -1; i = arc[i].next){
            int v = arc[i].v;
            if (!vis[v]){
                dfs(v);
                low[u] = min(low[u], low[v]);
            }
            else if (instack[v]){
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (low[u] == dfn[u]){
            ++ scc_num;
            while(st.top() != u){
                scc[st.top()] = scc_num;
                scc_acount[scc_num] ++;
                instack[st.top()] = 0;
                st.pop();
            }
            scc[st.top()] = scc_num;
            scc_acount[scc_num] ++;
            instack[st.top()] = 0;
            st.pop();
        }
        return ;
    }
    void tarjan(int n){
        mem(scc_acount, 0);
        mem(vis, 0);
        mem(instack, 0);
        mem(dfn, 0);
        mem(low, 0);
        mem(scc, 0);
        id = scc_num = 0;
        while(!st.empty())
            st.pop();
        for (int i = 1; i <= n; i ++){   //枚举节点
            if (!vis[i])
                dfs(i);
        }
        return ;
    }
}S;
/* ------------------------------ Tarjan ------------------------- */
int ca;
int odeg[MAXN];
int ideg[MAXN];
void solve(int n, int m){
    mem(odeg, 0);
    mem(ideg, 0);
    S.tarjan(n);
    if (S.scc_num == 1){
        printf("Case %d: %d\n", ca, -1);
        return ;
    }
    for (int i = 0; i < S.cnt; i ++){
        int u = S.arc[i].u;
        int v = S.arc[i].v;
        if (S.scc[u] != S.scc[v]){
            odeg[S.scc[u]] ++;
            ideg[S.scc[v]] ++;
        }
    }
    long long maxn = 0;
    for (int i = 1; i <= S.scc_num; i ++){
        if (ideg[i] == 0 || odeg[i] == 0){
            long long remain_num = n - S.scc_acount[i];
            long long num = (long long)S.scc_acount[i]*(S.scc_acount[i]-1) + (long long)remain_num*(remain_num-1) + (long long)remain_num*S.scc_acount[i] - m;
            if (num > maxn){
                maxn = num;
            }
        }
    }
    printf("Case %d: %I64d\n", ca, maxn);
}
int main(){
	//freopen("test.in", "r", stdin);
	//freopen("test.out", "w", stdout);
	int t;
	scanf("%d", &t);
	for (ca = 1; ca <= t; ca ++){
        int n, m;
        scanf("%d %d", &n, &m);
        S.init();
        for (int i = 0; i < m; i ++){
            int u, v;
            scanf("%d %d", &u, &v);
            S.add(u, v);
        }
        solve(n, m);
	}
	return 0;
}
posted @ 2013-08-01 21:48  AbandonZHANG  阅读(131)  评论(0编辑  收藏  举报