题意:
给一个有向的简单图(无重复边,无自环)...问该图是否是强连通图..若不是强联通问最多可以加多少条边..使得图依然不是强联通图并且还是简单图...
题解:
首先用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; }