割点桥双连通分量
Posted 惜取少年时
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了割点桥双连通分量相关的知识,希望对你有一定的参考价值。
HDU3844
引用训练指南上的题意模型“在一个无向图上选择尽量少的点涂黑,使得任意删除一个点后,每个连同分量至少有一个黑点”
对整个图分两种情况讨论:
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 }
以上是关于割点桥双连通分量的主要内容,如果未能解决你的问题,请参考以下文章