题意:
给一个 n 个节点,m 条边的有向图,求最大可以增加多少条边使得这个有向图仍然不是强连通的。
题解:
定理: 有向图中存在某点的入度或出度为零时,这张有向图不是强连通图
逆向思维
定义题中所给图为G1,一个有n个点的有向完全图为G2
最多可增加多少条边使得G1仍不是强连通 ⇒ 最少可删除多少条边使得G2不是强连通
缩点
构造一个n个点的有向完全图,需要n*(n-1)条边,而已经给出了m条,故先删去m条,剩下n*(n-1) - m条边
缩点后 假设有a个强连通块,任取其中一个强连通块,(假设取出的这个强连通块里有x个点),剩下的(n-a)个点看成一个强连通块,
如果让这两个强连通块之间不联通,肯定是这两个连通块只有一个方向的边,最多就会有x*(n-x)条边 所以最多加n*(n-1)-x*(n-x)-m边。
所以当x最小是式子最大。
即枚举所有出度或入度为0的强连通分量 求出所含点数的最小值(minn)
最后结果即为n * (n - 1) - m - minn * (n - minn)
add:最多可增加边的数量
del:最少可删除边的数量
m+add=n*(n-1)-del => add=n*(n-1)-m-del 帮助理解
如果刚开始图为强连通的输出 -1
借鉴自 链接
代码:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include<vector> #include <map> using namespace std; typedef long long ll; const int maxn = 1e5+5;//点数 const int maxm = 2e5+5;//边数,因为是无向图,所以这个值要*2 struct Edge { int to,next; bool cut;//是否是桥标记 } edge[maxm]; int head[maxn],tot; int low[maxn],dfn[maxn],Stack[maxn],belong[maxn];//belong数组的值是1~scc int Index,top; int scc;//边双连通块数/强连通分量的个数 bool Instack[maxn]; int bridge;//桥的数目 int cut[maxn]; int num[maxn]; void addedge(int u,int v) { edge[tot].to = v; edge[tot].next = head[u]; edge[tot].cut=false; head[u] = tot++; } void Tarjan(int u,int pre) { int v; low[u] = dfn[u] = ++Index; Stack[top++] = u; Instack[u] = true; int son=0; int flag=0; for(int i = head[u]; i != -1; i = edge[i].next) { v = edge[i].to; /* if(v == pre) { flag++; continue; }*/ if( !dfn[v] ) { son++; Tarjan(v,u); if( low[u] > low[v] )low[u] = low[v]; if(low[v] > dfn[u]) { bridge++; edge[i].cut = true; edge[i^1].cut = true; } if(u == pre && son > 1)cut[u] = true; if(u != pre && low[v] >= dfn[u])cut[u] = true; } else if( Instack[v] && low[u] > dfn[v] ) low[u] = dfn[v]; } if(low[u] == dfn[u]) { scc++; do { v = Stack[--top]; Instack[v] = false; belong[v] = scc; num[scc]++; } while( v!=u ); } } void init() { tot = 0; memset(head,-1,sizeof(head)); } int in[maxn],out[maxn]; void solve(int n) { memset(dfn,0,sizeof(dfn)); memset(Instack,false,sizeof(Instack)); memset(cut,0,sizeof cut); memset(num,0,sizeof num); memset(in,0,sizeof in); memset(out,0,sizeof out); Index = top = scc = 0; bridge = 0; for(int i = 1; i <= n; i++) if(!dfn[i]) Tarjan(i,i); } int main() { int t; int n,m; int Case=1; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); init(); int cnt=0; for(int i=1; i<=m; i++) { int u,v; scanf("%d%d",&u,&v); addedge(u,v); } solve(n); for(int u=1; u<=n; u++) { for(int i=head[u]; i!=-1; i=edge[i].next) { int v=edge[i].to; if(belong[u]==belong[v])continue; out[belong[u]]++; in[belong[v]]++; } } int minn=0x3f3f3f3f; for(int i=1; i<=scc; i++) { if(in[i]==0 || out[i]==0) { minn=min(minn,num[i]); } } ll ans=n*(n-1)-m; ans-=minn*(n-minn); if(scc==1)ans=-1; printf("Case %d: %lld\n",Case++,ans); } return 0; } /* 5 2 4 3 0 4 5 0 0 0 1 0 */