HDOJ 4635 - Strongly connected/2013多校联合第四场D Tarjan求强联通图.缩点.

         题意:

                  给一个有向的简单图(无重复边,无自环)...问该图是否是强连通图..若不是强联通问最多可以加多少条边..使得图依然不是强联通图并且还是简单图...

         题解:

                  首先用tarjan求强联通..判断整个图是否是一个强联通图....如果不是强联通..又要加尽可能多的边保持不强联通..有个方向是选出一块点集x作为独立的..这一块是个完全图..其他点集y也是一个完全图..整个图变成两个完全图..并且x每个点到y每个点都有单向边..保证了不强联通..那写出计算式..最后这个图的边数为: 

                  x*(x-1) + y*(y-1) +x*y = x^2+y^2+x*y-n = n^2-x*y+n

                  可见..要使得边数最多..x*y就要最小..x*y最小就要使得x与y的差距最大..所以就得出了要找一个点集最小的强联通块作为x..其他的点作为y...计算答案..但要注意..这个x必须是入度或者出度为0的点..否则就会出现y做完全图后导致整个图变成完全图了..也就是整个图强联通了...


Program:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#define ll long long
#define eps 1e-5
#define oo 1000000007
#define pi acos(-1.0)
#define MAXN 100005
using namespace std;  
struct node
{
       int x,y,next;
}line[MAXN];
int Lnum,_next[MAXN],dfn[MAXN],low[MAXN],tpnum,tp[MAXN],num[MAXN],DfsIndex;
bool In[MAXN],Out[MAXN];
bool instack[MAXN];
stack<int> mystack;
void addline(int x,int y)
{
       line[++Lnum].next=_next[x],_next[x]=Lnum;
       line[Lnum].x=x,line[Lnum].y=y;
}
void tarjan(int x)
{
       int y,k;
       dfn[x]=low[x]=++DfsIndex;
       instack[x]=true;
       mystack.push(x);
       for (k=_next[x];k;k=line[k].next)
       {
               y=line[k].y;
               if (!dfn[y])
               {
                        tarjan(y);
                        low[x]=min(low[x],low[y]);
               }else
               if (instack[y]) 
                        low[x]=min(low[x],dfn[y]);
       }
       if (low[x]==dfn[x])
       {
               tpnum++;
               num[tpnum]=0;
               do
               {
                        x=mystack.top();
                        num[tpnum]++;
                        mystack.pop();
                        tp[x]=tpnum;
                        instack[x]=false;
               }while (low[x]!=dfn[x]);
       }
       return; 
}
int main()
{       
       int cases,i,n,m,T;  
       scanf("%d",&T);
       for (cases=1;cases<=T;cases++)
       {
                scanf("%d%d",&n,&m);
                memset(_next,0,sizeof(_next));
                Lnum=0;
                for (i=1;i<=m;i++)
                {
                       int x,y;
                       scanf("%d%d",&x,&y);
                       addline(x,y);
                }
                memset(dfn,0,sizeof(dfn));
                memset(instack,false,sizeof(instack));
                while (!mystack.empty()) mystack.pop();
                tpnum=DfsIndex=0;
                for (i=1;i<=n;i++)
                   if (!dfn[i]) tarjan(i);
                printf("Case %d: ",cases);
                if (tpnum==1) 
                {
                       printf("-1\n");
                       continue;
                }
                memset(In,true,sizeof(In));
                memset(Out,true,sizeof(Out));
                for (i=1;i<=m;i++)
                {
                       int x=tp[line[i].x],y=tp[line[i].y];
                       if (x==y) continue;
                       Out[x]=false,In[y]=false;
                }
                ll x=oo,y; 
                for (i=1;i<=tpnum;i++)
                   if ((Out[i] || In[i]) && x>num[i])
                       x=num[i];
                y=n-x;
                printf("%I64d\n",x*y+x*(x-1)+y*(y-1)-m);        
       }
       return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值