HDU4635 Strongly connected(tarjan缩点+思路)

Strongly connected

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3476    Accepted Submission(s): 1402


Problem Description
Give a simple directed graph with N nodes and M edges. Please tell me the maximum number of the edges you can add that the graph is still a simple directed graph. Also, after you add these edges, this graph must NOT be strongly connected.
A simple directed graph is a directed graph having no multiple edges or graph loops.
A strongly connected digraph is a directed graph in which it is possible to reach any node starting from any other node by traversing edges in the direction(s) in which they point. 
 

Input
The first line of date is an integer T, which is the number of the text cases.
Then T cases follow, each case starts of two numbers N and M, 1<=N<=100000, 1<=M<=100000, representing the number of nodes and the number of edges, then M lines follow. Each line contains two integers x and y, means that there is a edge from x to y.
 

Output
For each case, you should output the maximum number of the edges you can add.
If the original graph is strongly connected, just output -1.
 

Sample Input
 
   
3 3 3 1 2 2 3 3 1 3 3 1 2 2 3 1 3 6 6 1 2 2 3 3 1 4 5 5 6 6 4
 

Sample Output
 
   
Case 1: -1 Case 2: 1 Case 3: 15


题意:给出一个有向图,问最多能添加多少条边,使得原图不是边双连通图,如果原本已经是边双连通图,输出-1

首先对于找最多的边,可以逆向考虑一下,先把图补满(即任意两点之间都有一条边)删除最少的边,对于原本已经有的边肯定是不能删的,首先在满的基础上减去原有的m条边,并用这m条边缩点,此时块与块之间的关系有了。然后只需要找出个点数最少的块,删去他与剩下所有块里的点之间的一条边即可,此时只能由点数少的块到点数大的块或点数大的块到点数小的块。因为x+y是个固定值时,x和y的差距越大,x*y越小。故此时删去边最少。

会有一个问题,这样删的时候会不会把原来的m条边删去?

其实是不会的,因为删边所用的图是由m条边缩点后的图,这m条边的关系只可能是每个块的内部与块与块之间,我们删去的是 点数最少块里的点与 其他所有块的点之间的一条边,每个块的点与另一个块之间依然相连至少一条边,而原本m条边,每个块的点与另一个块的点之间最多也只有一条边(两条边就连通了,缩点会直接缩到一个块里)

#include
#include
#include
#include
#include
#include
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=200005;
int low[N],dfn[N],dfs_num,vis[N];//tarjan
int tot,first[N];//邻接表
int col_num,top,block[N];//染色
int st[N];//染色与点双联通
int id[N],od[N],num[N];
struct edge
{
    int v,next;
} e[N*20];
void init(int n)
{
    mem(first,-1);
    mem(dfn,0);
    mem(low,0);
    mem(vis,0);
    mem(id,0);
    mem(od,0);
    mem(num,0);
    mem(st,0);
    tot=0,col_num=0,dfs_num=0,top=0;

}
void add(int u,int v)
{
    e[tot].v=v;
    e[tot].next=first[u];
    first[u]=tot++;
}
void dyeing(int u)//染色
{
    int v;
    if(low[u]==dfn[u])
    {
        ++col_num;
        do
        {
            v=st[--top];
            block[v]=col_num;
            num[col_num]++;
            vis[v]=0;
        }
        while(v!=u);
    }
}
void dfs(int u,int fa)
{
    low[u]=dfn[u]=++dfs_num;
    vis[u]=1;
    st[top++]=u;
    for(int i=first[u]; ~i; i=e[i].next)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            dfs(v,u);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v])
            low[u]=min(low[u],dfn[v]);
    }
    dyeing(u);//染色
}
void solve(int n,int m)
{
    for(int i=1; i<=n; i++)
    {
        if(!dfn[i])dfs(i,-1);
    }
    if(col_num==1)
    {
        puts("-1");
        return;
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=first[i]; ~j; j=e[j].next)
        {
            int v=e[j].v;
            if(block[i]==block[v])continue;
            od[block[i]]++;
            id[block[v]]++;
        }
    }
    ll ans=n*(n-1)-m;
    int minn=inf;
    for(int i=1;i<=col_num;i++)
        {
            if(!id[i]||!od[i])
            minn=min(minn,num[i]);
        }
        ans-=minn*(n-minn);
    printf("%lld\n",ans);
}
int main()
{
    int t,n,m,x,y,q=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init(n);
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        printf("Case %d: ",++q);
        solve(n,m);
    }
    return 0;
}


你可能感兴趣的:(【图的连通】)