题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4635
先判断图是否强连通。如果不是强连通的,那么缩点。
我们的目的是加最多的边,那么最后的图中,肯定两个集合,这两个集合都是强联通的,
一个集合到一个集合只有单向边。我们先让图是满图,然后通过删边来求的:有n*(n-1)条边,然后删掉已有的边m
,然后还有删掉两个集合的边n1*(n-n1),n1为其中一个集合的顶点个数,因为这里是单向边。
那么答案就是ans=n*(n-1)-m-n1*(n-n1),
我们要使ans最大,那么n1*(n-n1)就要越小
#include<stdio.h> #include<string.h> #include<vector> #include<algorithm> #define N 100005 #define INF 0xfffffff using namespace std; int head[N], cnt; int top, Is[N], Stack[N], low[N], dfn[N], Time, n, m; int nBlock, Block[N]; int Cnt[N], Out[N], In[N]; struct Edge { int v, next; }e[N]; void Init() { Time = cnt = top = nBlock = 0; memset(Cnt, 0, sizeof(Cnt)); memset(low, 0, sizeof(low)); memset(dfn, 0, sizeof(dfn)); memset(Stack,0 , sizeof(Stack)); memset(Is, 0, sizeof(Is)); memset(Out, 0, sizeof(Out)); memset(In, 0, sizeof(In)); memset(Block, 0, sizeof(Block)); memset(head, -1, sizeof(head)); } void Add(int u, int v) { e[cnt].v = v; e[cnt].next = head[u]; head[u] = cnt++; } void Tajar(int u, int father) { Stack[top++]=u; low[u] = dfn[u] = ++Time; Is[u] = 1; int v; for(int i=head[u]; i!=-1; i=e[i].next) { v = e[i].v; if(!dfn[v]) { Tajar(v, u); low[u] = min(low[u], low[v]); } else if(Is[v]) low[u] = min(low[u], dfn[v]); } if(low[u]==dfn[u]) { ++nBlock; do { v=Stack[--top]; Is[v] = 0; Block[v] = nBlock; Cnt[nBlock]++; }while(u!=v); } } int main() { int T, t=1, x, y; scanf("%d", &T); while(T--) { Init(); scanf("%d%d", &n, &m); for(int i=1; i<=m; i++) { scanf("%d%d", &x, &y); Add(x, y); } for(int i=1; i<=n; i++) { if(!low[i]) Tajar(i, -1); } for(int i=1; i<=n; i++) { for(int j=head[i]; j!=-1; j=e[j].next) { int u = Block[i]; int v = Block[e[j].v]; if(u != v) { Out[v]++; In[u]++; } } } y = INF; for(int i=1; i<=nBlock; i++) { if(!In[i] || !Out[i]) y=min(y, Cnt[i]); } x = n - y; long long ans=(long long)n*(n-1)-x*y-m; if(nBlock==1) printf("Case %d: -1\n", t++); else printf("Case %d: %lld\n", t++, ans); } return 0; }
时间: 2024-11-08 00:39:56