题目大意: 给你一个DAG图,问你最多能添加多少条边使得这个DAG图依然不是强联通的。
思路:
1、首先,对于一个简单图来说,如果其已经是一个强联通图了,那么就输出-1.
2、如果当前简单图不是强联通图,那么其要么存在强联通分量,要么不存在,如果不存在其就是一个DAG图,不需
要进行改变,如果存在强联通分量,那么对强联通分量进行染色,然后缩点,得到一个DAG图,所以以下对于问题的
讨论,也都是在一个DAG图的基础上来讨论的。
3、如果当前图已经是一个DAG图了,而且我们需要贪心的将边加入图中,其实我们枚举几组情况不难发现最终构成
的图无非两种情况:
其一:
其二:
贪心的方向就是这样的:找到一个出度为0,或者是入度为0的节点,然后让其他各个点都构成强联通分量,然后将这
个点和其他各个点连一条边。
那么将原来的强联通分量缩点染色之后贪心的方向就是这样的:找一个出度为0,或者是入度为0的节点,并且当前点
在原图中的点个数最少,然后将这个点和其他各个点连一条边。
4、然后考虑这样一个问题:将其他店构成强联通分量就能加最多的边吗?显然不是,让其形成一个完全子图才是最
贪心的方向。那么最终贪心思路就是这样的:
①找一个出度为0,或者是入度为0的节点,并且当前点在原图中的点个数最少。
②将其他各个点构成完全子图(一共需要边:n*n-1【这里n表示其他各点个数和】)
③将这个点内部也构成完全子图
③将这个点和其他各点相连
最终添加完边的图,肯定可以分成两个部X和Y,其中只有X到Y的边没有Y到X的边;
那么要使得边数尽可能的多,则X部肯定是一个完全图,Y部也是,同时X部中每个点到Y部的每个点都有一条边;
假设X部有x个点,Y部有y个点,则x+y=n;
同时边数F=x*y+x*(x-1)+y*(y-1),然后去掉已经有了的边m,则为答案;
当x+y为定值时,二者越接近,x*y越大,所以要使得边数最多,那么X部和Y部的点数的个数差距就要越大;
对于给定的有向图缩点,对于缩点后的每个点,如果它的出度或者入度为0,那么它才有可能成为X部或者Y部;
然后找出最大值即可;
#include
#include
#include
#include
using namespace std;
const int N = 100005;
const int M = 200005;
const int INF = 0xffffffff;
typedef long long ll;
int n, m, cnt, head[N];
int dep, top, atype;
int dfn[N], low[N], Stack[N], belong[N], in[N], out[N], sum[N];
bool vis[N];
struct Edge
{
int to, nxt;
}edge[M];
void addedge(int u, int v)
{
edge[cnt].to = v;
edge[cnt].nxt = head[u];
head[u] = cnt++;
}
void Tarjan(int u)
{
dfn[u] = low[u] = ++dep;
Stack[top++] = u;
vis[u] = true;
for(int i = head[u]; i != -1; i = edge[i].nxt)
{
int v = edge[i].to;
if(!dfn[v])
{
Tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(vis[v])
low[u] = min(low[u], dfn[v]);
}
int j;
if(dfn[u] == low[u])
{
atype++;
do
{
j = Stack[--top];
belong[j] = atype;
sum[atype]++;
vis[j] = false;
}
while(u != j);
}
}
void solve()
{
if(n == 1)
{
puts("-1");
return ;
}
cnt = dep = top = atype = 0;
memset(head, -1, sizeof(head));
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(vis, false, sizeof(vis));
memset(belong, 0, sizeof(belong));
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
memset(sum, 0, sizeof(sum));
int u, v;
for(int i = 0; i < m; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v);
}
for(int i = 1; i <= n; i++)
if(!dfn[i])
Tarjan(i);
if(atype == 1)
{
puts("-1");
return ;
}
for(int u = 1; u <= n; u++)
for(int i = head[u]; i != -1; i = edge[i].nxt)
{
int v = edge[i].to;
if(belong[u] != belong[v])
{
out[belong[u]]++;
in[belong[v]]++;
}
}
ll ans = 0, tmp;
for(int i = 1; i <= atype; i++)
if(in[i] == 0 || out[i] == 0)
{
tmp = sum[i];
ans = max(ans, tmp*(tmp-1) + (n-tmp)*(n-tmp-1) + tmp*(n-tmp) - m);
}
printf("%I64d\n", ans);
}
int main()
{
int T;
scanf("%d", &T);
for(int kase = 1; kase <= T; kase++)
{
scanf("%d%d", &n, &m);
printf("Case %d: ", kase);
solve();
}
return 0;
}