割点、桥、双连通分量、强连通分量

HDU 3844

引用训练指南上的题意模型“在一个无向图上选择尽量少的点涂黑,使得任意删除一个点后,每个连同分量至少有一个黑点”

对整个图分两种情况讨论:

1、整个连通图无割顶,即只有1个双连通分量。此时只需要任选2点染色即可

2、有若干的双连通分量。易证此时我们只需要在只有1个割顶的双连通分量中染任意非割顶的点。

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <algorithm>
  4 #include <vector>
  5 #include <set>
  6 #include <map>
  7 #include <string>
  8 #include <cstring>
  9 #include <stack>
 10 #include <queue>
 11 #include <cmath>
 12 #include <ctime>
 13 #include <bitset>
 14 #include <utility>
 15 #include <assert.h>
 16 using namespace std;
 17 #define rank rankk
 18 #define mp make_pair
 19 #define pb push_back
 20 #define xo(a,b) ((b)&1?(a):0)
 21 #define tm tmp
 22 //#pragma comment(linker, "/STACK:1024000000,1024000000") 
 23 //#define LL ll
 24 typedef unsigned long long ull;
 25 typedef pair<int,int> pii;
 26 typedef long long ll;
 27 typedef pair<ll,int> pli;
 28 typedef pair<ll,ll> pll;
 29 const int INF=0x3f3f3f3f;
 30 const ll INFF=0x3f3f3f3f3f3f3f3fll;
 31 const int MAX=4e5+10;
 32 //const ll MAXN=2e8;
 33 //const int MAX_N=MAX;
 34 const ll MOD=998244353;
 35 //const long double pi=acos(-1.0);
 36 //const double eps=0.00000001;
 37 ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
 38 template<typename T>inline T abs(T a) {return a>0?a:-a;}
 39 template<class T> inline
 40 void read(T& num) {
 41     bool start=false,neg=false;
 42     char c;
 43     num=0;
 44     while((c=getchar())!=EOF) {
 45         if(c=='-') start=neg=true;
 46         else if(c>='0' && c<='9') {
 47             start=true;
 48             num=num*10+c-'0';
 49         } else if(start) break;
 50     }
 51     if(neg) num=-num;
 52 }
 53 inline ll powMM(ll a,ll b,ll M){
 54     ll ret=1;
 55     a%=M;
 56 //    b%=M;
 57     while (b){
 58         if (b&1) ret=ret*a%M;
 59         b>>=1;
 60         a=a*a%M;
 61     }
 62     return ret;
 63 }
 64 void open()
 65 {
 66 //    freopen("1009.in","r",stdin);
 67     freopen("out.txt","w",stdout);
 68 }
 69 
 70 const int maxn=1e5+5;
 71 struct Edge{int u,v;};
 72 int pre[maxn],iscut[maxn],bccno[maxn],dfs_clock,bcc_cnt;//bccno记录每一点所属的BCC的编号
 73 vector<int> G[maxn],bcc[maxn];//bcc数组存储某一BCC中所有的点  G存原图
 74 stack<Edge>S;
 75 int dfs(int u,int fa)
 76 {
 77     int lowu=pre[u]=++dfs_clock;
 78     int child=0;
 79     for(int i=0;i<G[u].size();i++)
 80     {
 81         int v=G[u][i];
 82         Edge e=(Edge){u,v};
 83         if(!pre[v])//未访问过v
 84         {
 85             S.push(e);child++;
 86             int lowv=dfs(v,u);
 87             lowu=min(lowu,lowv);//用后代的low函数更新自己
 88             if(lowv>=pre[u])
 89             {
 90                 iscut[u]=true;//是割顶
 91                 bcc_cnt++;//双连通分量个数++
 92                 bcc[bcc_cnt].clear();//注意!bcc从1开始编号
 93                 for(;;)
 94                 {
 95                     Edge x=S.top();S.pop();
 96                     if(bccno[x.u]!=bcc_cnt){bcc[bcc_cnt].push_back(x.u);bccno[x.u]=bcc_cnt;}
 97                     if(bccno[x.v]!=bcc_cnt){bcc[bcc_cnt].push_back(x.v);bccno[x.v]=bcc_cnt;}
 98                     if(x.u==u&&x.v==v)break;//到了当前割顶的边
 99                 }
100             }
101         }
102         else if(pre[v]<pre[u]&&v!=fa)
103         {
104             S.push(e);lowu=min(lowu,pre[v]);//用反向边更新自己
105         }
106     }
107     if(fa<0&&child==1)iscut[u]=0;
108     return lowu;
109 }
110 void find_bcc(int n)
111 {
112     //调用结束后S保证为空 所以不用清空
113     memset(pre,0,sizeof(pre));
114     memset(iscut,0,sizeof(iscut));
115     memset(bccno,0,sizeof(bccno));
116     dfs_clock=bcc_cnt=0;
117     for(int i=1;i<=n;i++)//默认图顶点编号0开始 如果需要1开始可以直接更改
118         if(!pre[i])dfs(i,-1);
119 }
120 int n,x,y,da,cut_cnt,Case;
121 ll cnt,ge;
122 int main()
123 {
124     while(scanf("%d",&n)&&n)
125     {
126         ge=da=0;cnt=1;
127         for(int i=1;i<=(n+1);i++)G[i].clear();
128         for(int i=1;i<=n;i++){scanf("%d%d",&x,&y);G[x].pb(y);G[y].pb(x);da=max(da,x);da=max(da,y);}
129         find_bcc(da);
130         for(int i=1;i<=bcc_cnt;i++)
131         {
132             cut_cnt=0;
133             for(int j=0;j<bcc[i].size();j++)
134                 if(iscut[bcc[i][j]])++cut_cnt;
135             if(cut_cnt==1)
136                 ++ge,cnt*=(bcc[i].size()-1);
137         }
138         if(bcc_cnt==1)
139             ge=2,cnt=1LL*da*(da-1LL)/2LL;
140         printf("Case %d: %lld %lld\n",++Case,ge,cnt);
141     }
142     return 0;
143 }
View Code

 

HDU 2767

题意:n个点m条边的有向图,问最少添加多少条变使整个图强连通。

先求出所有强连通分量,对每个强连通分量缩点。新生成的图为DAG,(若原本的图已经为强连通,则输出0即可)设其中a个点出度为0,b个点入度为0,需要添加的边数即为max(a,b)

这也是有若干点的DAG,为使其强连通需要连的最少边数。

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <algorithm>
  4 #include <vector>
  5 #include <set>
  6 #include <map>
  7 #include <string>
  8 #include <cstring>
  9 #include <stack>
 10 #include <queue>
 11 #include <cmath>
 12 #include <ctime>
 13 #include <bitset>
 14 #include <utility>
 15 #include <assert.h>
 16 using namespace std;
 17 #define rank rankk
 18 #define mp make_pair
 19 #define pb push_back
 20 #define xo(a,b) ((b)&1?(a):0)
 21 #define tm tmp
 22 //#pragma comment(linker, "/STACK:1024000000,1024000000") 
 23 //#define LL ll
 24 typedef unsigned long long ull;
 25 typedef pair<int,int> pii;
 26 typedef long long ll;
 27 typedef pair<ll,int> pli;
 28 typedef pair<ll,ll> pll;
 29 const int INF=0x3f3f3f3f;
 30 const ll INFF=0x3f3f3f3f3f3f3f3fll;
 31 const int MAX=4e5+10;
 32 //const ll MAXN=2e8;
 33 //const int MAX_N=MAX;
 34 const ll MOD=998244353;
 35 //const long double pi=acos(-1.0);
 36 //const double eps=0.00000001;
 37 ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
 38 template<typename T>inline T abs(T a) {return a>0?a:-a;}
 39 template<class T> inline
 40 void read(T& num) {
 41     bool start=false,neg=false;
 42     char c;
 43     num=0;
 44     while((c=getchar())!=EOF) {
 45         if(c=='-') start=neg=true;
 46         else if(c>='0' && c<='9') {
 47             start=true;
 48             num=num*10+c-'0';
 49         } else if(start) break;
 50     }
 51     if(neg) num=-num;
 52 }
 53 inline ll powMM(ll a,ll b,ll M){
 54     ll ret=1;
 55     a%=M;
 56 //    b%=M;
 57     while (b){
 58         if (b&1) ret=ret*a%M;
 59         b>>=1;
 60         a=a*a%M;
 61     }
 62     return ret;
 63 }
 64 void open()
 65 {
 66 //    freopen("1009.in","r",stdin);
 67     freopen("out.txt","w",stdout);
 68 }
 69 
 70 const int maxn=1e5+5;
 71 vector<int> G[maxn];//存原图 初始化仅需初始此
 72 int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;//scc_cnt为SCC计数器 sccno[i]为i所在的SCC编号
 73 stack<int>S;
 74 void dfs(int u)
 75 {
 76     pre[u]=lowlink[u]=++dfs_clock;
 77     S.push(u);
 78     for(int i=0;i<G[u].size();i++)
 79     {
 80         int v=G[u][i];
 81         if(!pre[v])
 82         {
 83             dfs(v);
 84             lowlink[u]=min(lowlink[u],lowlink[v]);
 85         }
 86         else if(!sccno[v])
 87             lowlink[u]=min(lowlink[u],pre[v]);
 88     }
 89     if(lowlink[u]==pre[u])
 90     {
 91         scc_cnt++;
 92         for(;;)
 93         {
 94             int x=S.top();S.pop();
 95             sccno[x]=scc_cnt;
 96             if(x==u)break;
 97         }
 98     }
 99 }
100 void find_scc(int n)
101 {
102     dfs_clock=scc_cnt=0;
103     memset(sccno,0,sizeof(sccno));
104     memset(pre,0,sizeof(pre));
105     for(int i=1;i<=n;i++)
106         if(!pre[i])dfs(i);
107 }
108 int t,n,m,x,y;
109 int in[maxn],out[maxn],an,inn,ot;
110 int main()
111 {
112     scanf("%d",&t);
113     while(t--)
114     {
115         scanf("%d%d",&n,&m);
116         for(int i=1;i<=n;i++)G[i].clear();
117         for(int i=1;i<=m;i++)
118         {
119             scanf("%d%d",&x,&y);
120             G[x].pb(y);
121         }
122         find_scc(n);
123 //        cout<<"scc_num"<<scc_cnt<<endl;
124         memset(in,0,sizeof(in));
125         memset(out,0,sizeof(out));
126 //        memset(in,0,sizeof(int)*(scc_cnt+2));
127 //        memset(out,0,sizeof(int)*(scc_cnt+2));
128         for(int i=1;i<=n;i++)
129         {
130             for(int j=0;j<G[i].size();j++)
131             {
132                 int v=G[i][j];
133                 if(sccno[i]!=sccno[v])
134                     out[sccno[i]]=in[sccno[v]]=1;
135             }
136         }
137         inn=ot=0;
138         for(int i=1;i<=scc_cnt;i++)
139         {
140             if(!out[i])++ot;
141             if(!in[i])++inn;
142         }
143         if(scc_cnt==1)
144             printf("0\n");
145         else
146             printf("%d\n",max(ot,inn));
147     }
148     return 0;
149 }
View Code

 

UVA 11324

题意:给出一有向图G,求一个结点数最大的结点集,使得该结点集中任意两个结点u和v满足:要么u可以到达v,要么v可以到达u(v\u可互达亦可)

首先求出强连通分量,考虑任意强连通分量中一点,如果取了该点,显然应将该强连通分量中所有点取到才最优(并不影响除了该强连通分量中点以外其他点的选取)。如果不取该点,则该强连通分量一个点都不能选。(不然考虑某个选了的点,应该将该强连通分量全部选取,与该点未被选取矛盾)

故对于每个强连通分量均为全选或全不选。

求出强连通分量后缩点,每个新点的权值为该强连通分量的点数。对于新得到的DAG,求权值最大的路径即可。

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <algorithm>
  4 #include <vector>
  5 #include <set>
  6 #include <map>
  7 #include <string>
  8 #include <cstring>
  9 #include <stack>
 10 #include <queue>
 11 #include <cmath>
 12 #include <ctime>
 13 #include <bitset>
 14 #include <utility>
 15 #include <assert.h>
 16 using namespace std;
 17 #define rank rankk
 18 #define mp make_pair
 19 #define pb push_back
 20 #define xo(a,b) ((b)&1?(a):0)
 21 #define tm tmp
 22 //#pragma comment(linker, "/STACK:1024000000,1024000000") 
 23 //#define LL ll
 24 typedef unsigned long long ull;
 25 typedef pair<int,int> pii;
 26 typedef long long ll;
 27 typedef pair<ll,int> pli;
 28 typedef pair<ll,ll> pll;
 29 const int INF=0x3f3f3f3f;
 30 const ll INFF=0x3f3f3f3f3f3f3f3fll;
 31 const int MAX=4e5+10;
 32 //const ll MAXN=2e8;
 33 //const int MAX_N=MAX;
 34 const ll MOD=998244353;
 35 //const long double pi=acos(-1.0);
 36 //const double eps=0.00000001;
 37 ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
 38 template<typename T>inline T abs(T a) {return a>0?a:-a;}
 39 template<class T> inline
 40 void read(T& num) {
 41     bool start=false,neg=false;
 42     char c;
 43     num=0;
 44     while((c=getchar())!=EOF) {
 45         if(c=='-') start=neg=true;
 46         else if(c>='0' && c<='9') {
 47             start=true;
 48             num=num*10+c-'0';
 49         } else if(start) break;
 50     }
 51     if(neg) num=-num;
 52 }
 53 inline ll powMM(ll a,ll b,ll M){
 54     ll ret=1;
 55     a%=M;
 56 //    b%=M;
 57     while (b){
 58         if (b&1) ret=ret*a%M;
 59         b>>=1;
 60         a=a*a%M;
 61     }
 62     return ret;
 63 }
 64 void open()
 65 {
 66 //    freopen("1009.in","r",stdin);
 67     freopen("out.txt","w",stdout);
 68 }
 69 
 70 const int maxn=1e4+5;
 71 vector<int> G[maxn];//存原图 初始化仅需初始此
 72 int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;//scc_cnt为SCC计数器 sccno[i]为i所在的SCC编号
 73 stack<int>S;
 74 void dfs(int u)
 75 {
 76     pre[u]=lowlink[u]=++dfs_clock;
 77     S.push(u);
 78     for(int i=0;i<G[u].size();i++)
 79     {
 80         int v=G[u][i];
 81         if(!pre[v])
 82         {
 83             dfs(v);
 84             lowlink[u]=min(lowlink[u],lowlink[v]);
 85         }
 86         else if(!sccno[v])
 87             lowlink[u]=min(lowlink[u],pre[v]);
 88     }
 89     if(lowlink[u]==pre[u])
 90     {
 91         scc_cnt++;
 92         for(;;)
 93         {
 94             int x=S.top();S.pop();
 95             sccno[x]=scc_cnt;
 96             if(x==u)break;
 97         }
 98     }
 99 }
100 void find_scc(int n)
101 {
102     dfs_clock=scc_cnt=0;
103     memset(sccno,0,sizeof(sccno));
104     memset(pre,0,sizeof(pre));
105     for(int i=1;i<=n;i++)
106         if(!pre[i])dfs(i);
107 }
108 int t,n,m,x,y;
109 int dp[maxn],hav[maxn];
110 vector<int>E[maxn];
111 bool vi[maxn],used[maxn];
112 void dfs2(int now,int sum)
113 {
114     if(dp[now]>sum)return;
115     dp[now]=sum;
116     used[now]=true;
117     for(int i=0;i<E[now].size();i++)
118     {
119         int to=E[now][i];
120         if(!used[to]&&dp[to]<dp[now]+hav[to])
121             dfs2(to,dp[now]+hav[to]);
122     }
123     used[now]=false;
124 }
125 int main()
126 {
127     scanf("%d",&t);
128     while(t--)
129     {
130         scanf("%d%d",&n,&m);
131         for(int i=1;i<=n;i++)G[i].clear();
132         for(int i=1;i<=m;i++)
133         {
134             scanf("%d%d",&x,&y);
135             G[x].pb(y);
136         }
137         memset(dp,0,sizeof(dp));
138         memset(vi,0,sizeof(vi));
139         memset(hav,0,sizeof(hav));
140         for(int i=1;i<=n;i++)E[i].clear();
141         find_scc(n);
142         for(int i=1;i<=n;i++)
143         {
144             hav[sccno[i]]++;
145             for(int j=0;j<G[i].size();j++)
146             {
147                 int v=G[i][j];
148                 if(sccno[i]!=sccno[v])
149                 {
150                     E[sccno[i]].pb(sccno[v]);vi[sccno[v]]=true;
151                 }
152             }
153         }
154         for(int i=1;i<=scc_cnt;i++)dp[i]=hav[i];
155         for(int i=1;i<=scc_cnt;i++)
156                 dfs2(i,hav[i]);
157         int an=0;
158         for(int i=1;i<=scc_cnt;i++)an=max(an,dp[i]);
159         printf("%d\n",an);
160     }
161     return 0;
162 }
View Code

 

POJ3694

题意:给出连通的无向图。q次操作,每次将给定的两点连边。对于每次操作后的结果,输出当前图中的桥的数量。

对于初始的图,按边连通分量缩点。由于是连通图,缩点后形成的为一棵树。树结点间的边即为桥。每次给出的两点u、v,设其对应的边-连通分量分别为x、y,只要在树上将由x、y到其LCA的路径上的桥个数减掉即可。尽管连完新的边后,新的图中边-连通分量会发生改变,但我们并不需要维护这个值。只要维护每个桥是否被减掉过即可。

  1  #include <cstdio>
  2 #include <iostream>
  3 #include <algorithm>
  4 #include <vector>
  5 #include <set>
  6 #include <map>
  7 #include <string>
  8 #include <cstring>
  9 #include <stack>
 10 #include <queue>
 11 #include <cmath>
 12 #include <ctime>
 13 #include <bitset>
 14 #include <utility>
 15 #include <assert.h>
 16 using namespace std;
 17 #define rank rankk
 18 #define mp make_pair
 19 #define pb push_back
 20 #define xo(a,b) ((b)&1?(a):0)
 21 #define tm tmp
 22 //#pragma comment(linker, "/STACK:1024000000,1024000000") 
 23 //#define LL ll
 24 typedef unsigned long long ull;
 25 typedef pair<int,int> pii;
 26 typedef long long ll;
 27 typedef pair<ll,int> pli;
 28 typedef pair<ll,ll> pll;
 29 const int INF=0x3f3f3f3f;
 30 const ll INFF=0x3f3f3f3f3f3f3f3fll;
 31 const int MAX=5e5+10;
 32 //const ll MAXN=2e8;
 33 //const int MAX_N=MAX;
 34 const ll MOD=1e9+7;
 35 //const long double pi=acos(-1.0);
 36 //const double eps=0.00000001;
 37 ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
 38 template<typename T>inline T abs(T a) {return a>0?a:-a;}
 39 template<class T> inline
 40 void read(T& num) {
 41     bool start=false,neg=false;
 42     char c;
 43     num=0;
 44     while((c=getchar())!=EOF) {
 45         if(c=='-') start=neg=true;
 46         else if(c>='0' && c<='9') {
 47             start=true;
 48             num=num*10+c-'0';
 49         } else if(start) break;
 50     }
 51     if(neg) num=-num;
 52 }
 53 inline ll powMM(ll a,ll b,ll M){
 54     ll ret=1;
 55     a%=M;
 56 //    b%=M;
 57     while (b){
 58         if (b&1) ret=ret*a%M;
 59         b>>=1;
 60         a=a*a%M;
 61     }
 62     return ret;
 63 }
 64 void open()
 65 {
 66 //    freopen("1009.in","r",stdin);
 67     freopen("out.txt","w",stdout);
 68 }
 69 
 70 const int MAXN=1e5+5;//点数
 71 const int MAXM=4e5+5;//边数,因为是无向图,所以值要*2
 72 struct Edge
 73 {
 74     int to,next;
 75     bool cut;//是否是桥标记
 76 }edge[MAXM];
 77 int head[MAXN],tot;
 78 int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];//Belong数组的值是1——block
 79 int Index,top;
 80 int block;//边双连通分量个数
 81 bool Instack[MAXN];
 82 int bridge;//桥的数目
 83 void addedge(int u,int v)
 84 {
 85     edge[tot].to=v;edge[tot].next=head[u];edge[tot].cut=false;head[u]=tot++;
 86 }
 87 void Tarjan(int u,int pre)//调用此之前调用init初始化各个变量
 88 {
 89     int v;
 90     Low[u]=DFN[u]=++Index;
 91     Stack[top++]=u;Instack[u]=true;
 92     for(int i=head[u];i!=-1;i=edge[i].next)
 93     {
 94         v=edge[i].to;
 95         if(v==pre)continue;
 96         if(!DFN[v])
 97         {
 98             Tarjan(v,u);
 99             if(Low[u]>Low[v])Low[u]=Low[v];
100             if(Low[v]>DFN[u])//u的后代只能连回v自己 则u、v是桥
101             {
102                 bridge++;edge[i].cut=true;
103                 edge[i^1].cut=true;
104             }
105         }
106         else if(Instack[v]&&Low[u]>DFN[v])
107             Low[u]=DFN[v];
108     }
109     if(Low[u]==DFN[u])
110     {
111         block++;
112         do
113         {
114             v=Stack[--top];Instack[v]=false;Belong[v]=block;
115         }
116         while(v!=u);
117     }
118 }
119 void init()
120 {
121     tot=Index=top=block=bridge=0;
122     memset(head,-1,sizeof(head));
123 //    memset(Instack,false,sizeof(Instack));
124     memset(DFN,0,sizeof(DFN));
125 }
126 int n,m,u,v,Case,an,q;
127 vector<int>son[MAXN];
128 int fa[MAXN];
129 int a[MAXN],dep[MAXN];
130 queue<int>que;
131 void init_bfs(int rt)
132 {
133     memset(dep,-1,sizeof(dep));
134     dep[rt]=0;a[rt]=0;fa[rt]=-1;
135     que.push(rt);
136     while(!que.empty())
137     {
138         int now=que.front();que.pop();
139         for(int i=0;i<son[now].size();i++)
140         {
141             int to=son[now][i];
142             if(dep[to]!=-1)continue;
143             dep[to]=dep[now]+1;
144             a[to]=1;
145             fa[to]=now;
146             que.push(to);
147         }
148     }
149 }
150 void lca(int u,int v)
151 {
152     if(dep[u]>dep[v])swap(u,v);
153     while(dep[u]<dep[v])
154     {
155         if(a[v])
156         {
157             --an;a[v]=0;
158         }
159         v=fa[v];
160     }
161     while(u!=v)
162     {
163         if(a[u])
164         {
165             --an;a[u]=0;
166         }
167         if(a[v])
168         {
169             --an;a[v]=0;
170         }
171         u=fa[u];v=fa[v];
172     }
173 }
174 int main()
175 {
176     while(scanf("%d%d",&n,&m)&&n)
177     {
178         init();
179         memset(a,0,sizeof(a));
180 //        memset(Belong,0,sizeof(Belong));
181         for(int i=1;i<=m;i++)
182         {
183             scanf("%d%d",&u,&v);
184             addedge(u,v);addedge(v,u);
185         }
186         Tarjan(1,0);
187         for(int i=1;i<=block;i++)son[i].clear();
188         for(int i=1;i<=n;i++)
189             for(int j=head[i];j!=-1;j=edge[j].next)
190                 if(edge[j].cut)
191                 {
192                     int v=edge[j].to;
193                     son[Belong[i]].pb(Belong[v]);
194                     son[Belong[v]].pb(Belong[i]);
195                 }
196         init_bfs(1);
197         an=block-1;
198         scanf("%d",&q);
199         printf("Case %d:\n",++Case);
200         while(q--)
201         {
202             scanf("%d%d",&u,&v);
203             lca(Belong[u],Belong[v]);
204             printf("%d\n",an);
205         }
206         printf("\n");
207 
208     }
209 }
210 /*
211 8
212 11011011
213 */
View Code

 

posted @ 2017-10-08 12:18  perplex  阅读(325)  评论(0编辑  收藏  举报