这道题其实做完之后发现几乎就是模板题,然而之前有个地方想了一周一直拧不过来,题目是最多加多少条边仍然不是强连通图,也就可以理解为再多加一条边就无论如何都是强连通图,等价于最多去掉几条边使之仍未强连通图,当此时再多去一条边的时候,等价于最少去掉几条变使原图不强连通,好了终于绕完了,都给我自己绕蒙了,那么我们是最少去掉多少条边使原图不强联通,我们先把它变成一个完全图,然后再减去题目中给的边
ans=n*(n-1)-m-x,至于这个x是多少呢,就是我最少要去掉的边数,因为x=(n-a)*a,n是一定的a和(n-a)差越大,二者乘积就越小,ans就越大,所以我们要做的就是把那些缩点分成两个集合,一个集合里是所有缩点中点的个数最小的集合,其他的缩点放在另一个集合里,并且那个单独在一个集合的缩点,一定要满足入度或出度为0,因为只有这样才能保证这个边是只有一个方向的,也就是没有环,不是强连通的
还有就是判-1,只用团的个数,最终结果可能等于0!
#include <iostream>
#include <vector>
#include <stdio.h>
#include <string.h>
#include <stack>
using namespace std;
const int maxn=200005;
vector <int>G[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
stack<int>S;
void dfs(int u)
{
pre[u]=lowlink[u]=++dfs_clock;
S.push(u);
for(int i=0; i<G[u].size(); i++)
{
int v=G[u][i];
if(!pre[v])
{
dfs(v);
lowlink[u]=min(lowlink[u],lowlink[v]);
}
else if(!sccno[v])
{
lowlink[u]=min(lowlink[u],pre[v]);
}
}
if(lowlink[u]==pre[u])
{
scc_cnt++;
for(;;)
{
int x=S.top();
S.pop();
sccno[x]=scc_cnt;
if(x==u)
break;
}
}
}
void find_scc(int n)
{
dfs_clock=scc_cnt=0;
memset(sccno,0,sizeof(sccno));
memset(pre,0,sizeof(pre));
for(int i=0; i<n; i++)if(!pre[i])dfs(i);
}
int in0[maxn],out0[maxn];
int main()
{
int T,n,m,cas=1;
int num[maxn];
scanf("%d",&T);
while(T--)
{
memset(in0,0,sizeof(in0));
memset(out0,0,sizeof(out0));
memset(num,0,sizeof(num));
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++)
G[i].clear();
for(int i=0; i<m; i++)
{
int u,v;
scanf("%d%d",&u,&v);
u--;
v--;
G[u].push_back(v);
}
find_scc(n);
for(int i=1; i<=scc_cnt; i++)
in0[i]=out0[i]=1;
for(int u=0; u<n; u++)
{
for(int i=0; i<G[u].size(); i++)
{
int v=G[u][i];
if(sccno[u]!=sccno[v])
in0[sccno[v]]=out0[sccno[u]]=0;
}
num[sccno[u]]++;
}
int maxnum=maxn;
for(int i=1; i<=scc_cnt; i++)
{
if(in0[i]==1||out0[i]==1)
{
if(num[i]<maxnum)
maxnum=num[i];
}
}
//cout<<maxnum<<endl;
long long ans=n*(n-1)-m-(n-maxnum)*maxnum;
if(scc_cnt!=1) printf("Case %d: %lld\n",cas++,ans);
else printf("Case %d: -1\n",cas++);
}
return 0;
}
时间: 2024-10-26 11:51:15