题目大意:给你一个简单图,问最多还能加多少条有向边,使得图还是一个简单图。(问最多能够加多少条边至图还不是强联通图)
思路:
1、首先,对于一个简单图来说,如果其已经是一个强联通图了,那么就输出-1.
2、如果当前简单图不是强联通图,那么其要么存在强联通分量,要么不存在,如果不存在其就是一个DAG图,不需要进行改变,如果存在强联通分量,那么对强联通分量进行染色,然后缩点,得到一个DAG图,所以以下对于问题的讨论,也都是在一个DAG图的基础上来讨论的。
3、如果当前图已经是一个DAG图了,而且我们需要贪心的将边加入图中,其实我们枚举几组情况不难发现最终构成的图无非两种情况:
其一:
其二:
贪心的方向就是这样的:找到一个出度为0,或者是入度为0的节点,然后让其他各个点都构成强联通分量,然后将这个点和其他各个点连一条边。
那么将原来的强联通分量缩点染色之后贪心的方向就是这样的:找一个出度为0,或者是入度为0的节点,并且当前点在原图中的点个数最少,然后将这个点和其他各个点连一条边。
4、然后考虑这样一个问题:将其他店构成强联通分量就能加最多的边吗?显然不是,让其形成一个完全子图才是最贪心的方向。那么最终贪心思路就是这样的:
①找一个出度为0,或者是入度为0的节点,并且当前点在原图中的点个数最少。
②将其他各个点构成完全子图(一共需要边:n*n-1【这里n表示其他各点个数和】)
③将这个点内部也构成完全子图
③将这个点和其他各点相连。
那么整体思路就构建完成了,最后剩下的步骤就是实现代码了。注意点多的时候需要long long类型、
Ac代码:
#include
#include
#include
#include
using namespace std;
#define ll __int64
int head[200000];
int vis[200000];
int color[200000];
int stack[200000];
int dfn[200000];
int low[200000];
ll in[200000];
ll out[200000];
ll num[200000];
ll bian[200000];
struct edge
{
int from;
int to;
int w;
int next;
}e[1100000];
int n,m,cont,tt,cnt,sig;
void add(int from,int to)
{
e[cont].to=to;
e[cont].next=head[from];
head[from]=cont++;
}
void Tarjan(int u)
{
vis[u]=1;
dfn[u]=low[u]=cnt++;
stack[++tt]=u;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(vis[v]==0)Tarjan(v);
if(vis[v]==1)low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u])
{
sig++;
do
{
vis[stack[tt]]=-1;
color[stack[tt]]=sig;
}
while(stack[tt--]!=u);
}
}
void Slove()
{
sig=0;
cnt=1;
tt=-1;
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(bian,0,sizeof(bian));
memset(num,0,sizeof(num));
memset(vis,0,sizeof(vis));
memset(color,0,sizeof(color));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(stack,0,sizeof(stack));
for(int i=1;i<=n;i++)
{
if(vis[i]==0)Tarjan(i);
}
if(sig==1)
{
printf("-1\n");
return ;
}
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
num[color[i]]++;
}
for(int i=1;i<=n;i++)
{
for(int j=head[i];j!=-1;j=e[j].next)
{
int u=i,v=e[j].to;
if(color[u]==color[v])
{
bian[color[u]]++;
}
if(color[u]!=color[v])
{
in[color[v]]++;
out[color[u]]++;
}
}
}
ll minn=0x3f3f3f3f;
int pos;
for(int i=1;i<=sig;i++)
{
if(in[i]==0||out[i]==0)
{
if(minn>num[i])
{
minn=num[i];
pos=i;
}
}
}
//找到那个点。
ll output=num[pos]*(num[pos]-1)-bian[pos];
ll tmp=0;
for(int i=1;i<=sig;i++)
{
if(i!=pos)
{
tmp+=num[i];
}
}
output+=tmp*(tmp-1);
output+=num[pos]*tmp;
for(int i=1;i<=n;i++)
{
for(int j=head[i];j!=-1;j=e[j].next)
{
int u=i,v=e[j].to;
if(color[u]!=pos&&color[v]!=pos)
{
output--;
}
if(color[u]!=pos&&color[v]==pos)
{
output--;
}
if(color[u]==pos&&color[v]!=pos)
{
output--;
}
}
}
printf("%I64d\n",output);
}
int main()
{
int t;
int kase=0;
scanf("%d",&t);
while(t--)
{
cont=0;
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=0;i