http://acm.hdu.edu.cn/showproblem.php?pid=4635
3 3 3 1 2 2 3 3 1 3 3 1 2 2 3 1 3 6 6 1 2 2 3 3 1 4 5 5 6 6 4
Case 1: -1 Case 2: 1 Case 3: 15
最终添加完边的图,肯定可以分成两个部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),整理得:F=N*N-N-x*y,当x+y为定值时,二者越接近,x*y越大,所以要使得边数最多,那么X部和Y部的点数的个数差距就要越大,所以首先对于给定的有向图缩点,对于缩点后的每个点,如果它的出度或者入度为0,那么它才有可能成为X部或者Y部,所以只要求缩点之后的出度或者入度为0的点中,包含节点数最少的那个点,令它为一个部,其它所有点加起来做另一个部,就可以得到最多边数的图了
#include<iostream> #include<cstring> #include<cstdio> using namespace std; #define prt(k) cout<<#k"="<<k<<endl; #define ll long long #include<vector> #define N 100053 int belong[N],dfn[N],low[N]; vector<int> g[N]; #include<stack> int scc,curr; stack<int> S; bool instack[N]; vector<int> color[N],G1[N],G2[N]; int n,m; void dfs(int u) { dfn[u]=low[u]=++curr; S.push(u); instack[u]=1; for(int i=0;i<g[u].size();i++) { int v=g[u][i]; if(!dfn[v]) { dfs(v); low[u]=min(low[u],low[v]); } else { if(instack[v]) low[u]=min(low[u],dfn[v]); } } if(low[u]==dfn[u]) { ++scc; int v; do { v=S.top(); S.pop(); color[scc].push_back(v); instack[v]=0; belong[v]=scc; }while(v!=u); } } int main() { int tt; cin>>tt; int ca=1; while(tt--) { scanf("%d%d",&n,&m); for(int i=0;i<=n;i++) g[i].clear(),G1[i].clear(),G2[i].clear(),color[i].clear(); for(int i=0;i<m;i++) { int u,v; scanf("%d%d",&u,&v); g[u].push_back(v); } memset(dfn,0,sizeof dfn); memset(instack,0,sizeof instack); while(!S.empty()) S.pop(); scc=0; curr=0; for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i); /// prt(scc); printf("Case %d: ",ca++); if(scc==1) { puts("-1"); continue; } for(int u=1;u<=n;u++) { for(int i=0;i<g[u].size();i++) { int v=g[u][i]; int a=belong[u],b=belong[v]; /// prt(a); prt(b); if(a!=b) G1[a].push_back(b),G2[b].push_back(a); /// puts("======="); } } int X=n; for(int i=1;i<=scc;i++) if(G1[i].size()==0||G2[i].size()==0) { X=min(X,(int)color[i].size()); } ll ans=1ll*n*n-n-1ll*X*(n-X)-m; printf("%I64d\n",ans); } }