hdu4635——Strongly connected



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
 

Source
2013 Multi-University Training Contest 4

这题我没有想出来,看了题解才知道怎么做的。
果然很渣-.-

    如果要让边数尽可能多,那么最后一定是再加一条边整个图就成为强连通图的情况,所以最后我们一定可以把图分成x,y两份,其中x是完全图,y是完全图,x每个点到y每个点都有边,但y到x没有(如果有的话,整个图就是强连通图了),所以边==x*(x-1)+y*(y-1)+x*y-m;整理以后就是n*n-n-m-x*y;现在要让这个值尽可能大,那么x,y差值就要最大,
如果x,y差值太小,他们的积相对就会比较大,那么接下来我们就要把一个强连通分量缩成一个点,如果这个强连通分量可以成为x或者y,那么它的出度或者入度一定是0,否则如果都不为0,那么就可以和y(x)合在一起使得图成为强连通图。所以我们只要在这种情况下去一个边数最大值就行了。

#include<stdio.h>
#include<string.h>

const int maxn=100050;
int DFN[maxn];//记录每个点被访问到的时间
int low[maxn];//记录点可以直接或间接到达的最早被访问到的点(也就是那个强连通分量的
int stack[maxn];
int sccnum[maxn];//标记每个点属于第几个强连通分量
int in[maxn],out[maxn];//出度和入度
bool instack[maxn];
int num[maxn];
int sccNum;//强连通分量的数目
int top;
int index;
int n;
struct node
{
    int to;
    int next;
}edge[maxn];
int head[maxn];
int tot;

long long max(long long a,long long b)
{
	return a>b?a:b;
}

void addedge(int from,int to)
{
    edge[tot].to=to;
    edge[tot].next=head[from];
    head[from]=tot++;
}

void tarjan(int i)
{
    DFN[i]=low[i]=++index;//刚刚搜到这个点,DFN和low都赋值为被访问到的时间
    stack[top++]=i;//入栈
    instack[i]=1;
    for (int j=head[i];j!=-1;j=edge[j].next)
    {
        if (!DFN[edge[j].to])//如果没有被访问过
        {
            tarjan(edge[j].to);
            //这个时候low可能要修改,值为i或者i的子树可以到达的最早被访问到的点的时
            if (low[i]>low[edge[j].to])
                low[i]=low[edge[j].to];
        }
        else if (instack[edge[j].to])//已经在栈
        {
            if (low[i]>DFN[edge[j].to])
                low[i]=DFN[edge[j].to];
        }
    }
    if (DFN[i]==low[i])//找到根
    {
    	sccNum++;
    	int v;
        do
        {
            v=stack[--top];
            sccnum[v]=sccNum;
            num[sccNum]++;
            instack[v]=0;//标记出栈
        }while(v!=i);
    }
}

void solve()
{
    memset(DFN,0,sizeof(DFN));
    memset(instack,0,sizeof(instack));
    memset(num,0,sizeof(num));
    index=0;
    sccNum=0;
    top=0;
    for (int i=1;i<=n;i++)
        if (!DFN[i])
            tarjan(i);
}

int main()
{
    int m,a,b,t;
    scanf("%d",&t);
    int icase=1;
    while (t--)
    {
        scanf("%d%d",&n,&m);
        if (n==0 && m==0)
            break;
        memset(head,-1,sizeof(head));
        tot=0;
        for (int i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            addedge(a,b);
        }
        solve();
        printf("Case %d: ",icase++);
        if (sccNum==1)
            printf("-1\n");
        else
        {
            memset(in,0,sizeof(in));
            memset(out,0,sizeof(out));
            for (int i=1;i<=n;i++)
                for (int j=head[i];j!=-1;j=edge[j].next)
                {
                    int v=edge[j].to;
                    if (sccnum[v]!=sccnum[i])
                    {
                        out[sccnum[i]]++;
                        in[sccnum[v]]++;
                    }
                }
            long long cur=(long long)n*(n-1)-m,ans=-1;
            for (int i=1;i<=sccNum;i++)
            {
                if (out[i]==0 || in[i]==0)
                	ans=max(ans,cur-(n-num[i])*num[i]);
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值