割点、桥、双连通分量、强连通分量
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 }
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 }
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 }
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 */