HDU 4635 Strongly connected(强连通分量,变形)

 

题意:给出一个有向图(不一定连通),问最多可添加多少条边而该图仍然没有强连通。

思路:

  强连通分量必须先求出,每个强连通分量包含有几个点也需要知道,每个点只会属于1个强连通分量。

  在使图不强连通的前提下,要添加尽可能多的边。边至多有n*(n-1)条,而已经给了m条,那么所能添加的边数不可能超过k=n*(n-1)-m

  这k条边还有部分不能添加,一添加立刻就强连通。一个强连通图最少只需要n条边,根据强连通的特性,缩点之后必定是不会有环的存在的,那么只要继续保持没有环的存在即可。我们只要让其中1个强连通分量没有出度,或者没有入度就行了。到底选哪个强连通分量?选点数少的,这点在后面会了解。因为1*10=10,而2*9=18啊,也就是两个数越接近,两者之积越大。

  如果挑出1个出度(入度也行的,同理)为0的强连通分量,该强连通分量的点数为d,那么答案就是k-d*(n-d),即不允许有其他n-d个点有任何一条边指向此强连通分量,其他边都是可以存在的。那么要使得k-d*(n-d)最大,那么d*(n-d)要最小,就是上面讲的要选哪个强连通分量的原因。

  那么应该找到缩点后出/入度为0的且包含点数最少的强连通分量来计算即可。

 

 

 

 1 #include <bits/stdc++.h>
 2 #define LL long long
 3 #define pii pair<int,int>
 4 using namespace std;
 5 const int N=100000+5;
 6 const int INF=0x7f7f7f7f;
 7 vector<int> vect[N];
 8 stack<int> stac;
 9 int num[N], chu[N], ru[N];         //统计强连通分量中点的个数
10 int scc_no[N], lowlink[N], dfn[N], dfn_clock, scc_cnt;  //SCC必备
11 int n, m;
12 unordered_map<int,int>  mapp;
13 void DFS(int x) //常规tarjan
14 {
15     stac.push(x);
16     dfn[x]=lowlink[x]=++dfn_clock;
17     for(int i=0; i<vect[x].size(); i++)
18     {
19         int t=vect[x][i];
20         if(!dfn[t])
21         {
22             DFS(t);
23             lowlink[x]=min(lowlink[x],lowlink[t]);
24         }
25         else if(!scc_no[t]) //如果t不属于任何一个强连通分量
26             lowlink[x]=min(lowlink[x],dfn[t]);
27     }
28     if(lowlink[x]==dfn[x])
29     {
30         scc_cnt++;
31         while(true)
32         {
33             int t=stac.top();
34             stac.pop();
35             scc_no[t]=scc_cnt;
36             //scc[scc_cnt].push_back(t);
37             if(t==x)    break;
38         }
39     }
40 }
41 
42 
43 LL cal()
44 {
45     memset(dfn,0,sizeof(dfn));
46     memset(lowlink,0,sizeof(lowlink));
47     memset(scc_no,0,sizeof(scc_no));
48 
49     dfn_clock=scc_cnt=0;
50     for(int i=1; i<=n; i++)    if(!dfn[i])    DFS(i);   //深搜
51     if(scc_cnt==1)  return -1;  //已经强连通
52 
53     memset(chu,0,sizeof(chu));
54     memset(ru,0,sizeof(ru));
55     for(int i=1; i<=n; i++) //统计出入度数量
56         for(int j=0; j<vect[i].size(); j++)
57             if(scc_no[i]!=scc_no[vect[i][j]] )  chu[scc_no[i]]=ru[scc_no[vect[i][j]]]=true;
58 
59     memset(num,0,sizeof(num));
60     for(int i=1; i<=n; i++)    num[scc_no[i]]++;    //统计每个scc中的点的数目
61     int small=INF;
62     for(int i=1; i<=scc_cnt; i++)    if(!chu[i]||!ru[i])   small=min(small,num[i]);//挑量小的,只要满足出/入度为0。如果都已经有出和入度了,那还计算啥!
63     return (LL)n*(n-1) - m - small*(n-small);   //注意10万*10万
64 }
65 
66 
67 int main()
68 {
69     freopen("input.txt", "r", stdin);
70     int a, b, t, j=0;
71     cin>>t;
72     while(t--)
73     {
74         scanf("%d%d",&n,&m);
75         mapp.clear();
76         int up=0;
77         for(int i=1; i<=n; i++) vect[i].clear();
78         for(int i=0; i<m; i++)
79         {
80             scanf("%d%d", &a, &b);
81             if(!mapp[a])    mapp[a]=++up;   //只是怕点号不连续,好像多余了
82             if(!mapp[b])    mapp[b]=++up;
83             vect[mapp[a]].push_back(mapp[b]);
84         }
85         printf("Case %d: %lld\n",++j,cal());
86     }
87     return 0;
88 }
AC代码

 

  

posted @ 2015-07-07 22:19  xcw0754  阅读(176)  评论(0编辑  收藏  举报