tarjan
专业求解强连通分量边双连通分量点双连通分量缩点割点割边30年!
“你是割点”
“那你就是桥”
“是的,我是一条归桥,想不到吧”
首先是强连通分量缩点的tarjan。奉上代码。
1 #include <cstdio> 2 #include <cstring> 3 #include <stack> 4 5 const int N = 500010, INF = 0x7f7f7f7f; 6 7 inline void read(int &x) { 8 x = 0; 9 char c = getchar(); 10 bool f = 0; 11 while(c < '0' || c > '9') { 12 if(c == '-') { 13 f = 1; 14 } 15 c = getchar(); 16 } 17 while(c >= '0' && c <= '9') { 18 x = (x << 3) + (x << 1) + c - 48; 19 c = getchar(); 20 } 21 if(f) { 22 x = (~x) + 1; 23 } 24 return; 25 } 26 27 struct Edge { 28 int nex, v; 29 }edge[N], scc_edge[N]; int tp, scc_tp; 30 31 int stk[N], top; 32 int e[N], scc_e[N], scc_cnt, val[N], scc_val[N], dfn[N], low[N], num; 33 int n, m, fr[N], in[N], f[N]; 34 bool use[N], scc_use[N]; 35 36 inline void add(int x, int y) { 37 tp++; 38 edge[tp].v = y; 39 edge[tp].nex = e[x]; 40 e[x] = tp; 41 return; 42 } 43 44 inline void scc_add(int x, int y) { 45 scc_tp++; 46 scc_edge[scc_tp].v = y; 47 scc_edge[scc_tp].nex = scc_e[x]; 48 scc_e[x] = scc_tp; 49 return; 50 } 51 52 inline void exmin(int &a, const int &b) { 53 a > b ? a = b : 0; 54 return; 55 } 56 inline void exmax(int &a, const int &b) { 57 a < b ? a = b : 0; 58 return; 59 } 60 61 void tarjan(int x) { 62 low[x] = dfn[x] = ++num; 63 stk[++top] = x; 64 for(int i = e[x]; i; i = edge[i].nex) { 65 int y = edge[i].v; 66 if(!dfn[y]) { 67 tarjan(y); 68 exmin(low[x], low[y]); 69 } 70 else if(!fr[y]) { 71 exmin(low[x], dfn[y]); 72 } 73 } 74 if(low[x] == dfn[x]) { 75 scc_cnt++; 76 int y; 77 do { 78 y = stk[top]; 79 top--; 80 fr[y] = scc_cnt; 81 scc_val[scc_cnt] += val[y]; 82 scc_use[scc_cnt] |= use[y]; 83 } while(y != x); 84 } 85 return; 86 } 87 88 int main() { 89 90 //freopen("atm.in", "r", stdin); 91 //freopen("atm.out", "w", stdout); 92 93 read(n); read(m); 94 for(int i = 1, x, y; i <= m; i++) { 95 read(x); read(y); 96 add(x, y); 97 } 98 for(int i = 1; i <= n; i++) { 99 read(val[i]); 100 } 101 int s, k; 102 read(s); read(k); 103 for(int i = 1, x; i <= k; i++) { 104 read(x); 105 use[x] = 1; 106 } 107 108 tarjan(s); 109 for(int x = 1; x <= n; x++) { 110 if(!dfn[x]) { 111 continue; 112 } 113 for(int i = e[x]; i; i = edge[i].nex) { 114 int y = edge[i].v; 115 if(fr[y] != fr[x]) { 116 scc_add(fr[x], fr[y]); 117 in[fr[y]]++; 118 } 119 } 120 } 121 122 memset(f, ~0x7f, sizeof(f)); 123 f[fr[s]] = 0; 124 int ans = -INF; 125 for(int i = 1; i <= scc_cnt; i++) { 126 if(!in[i]) { 127 stk[++top] = i; 128 } 129 } 130 while(top) { 131 int x = stk[top]; 132 top--; 133 f[x] += scc_val[x]; 134 if(scc_use[x]) { 135 exmax(ans, f[x]); 136 } 137 for(int i = scc_e[x]; i; i = scc_edge[i].nex) { 138 int y = scc_edge[i].v; 139 in[y]--; 140 exmax(f[y], f[x]); 141 if(!in[y]) { 142 stk[++top] = y; 143 } 144 } 145 } 146 147 printf("%d\n", ans); 148 return 0; 149 }
然后是边双连通分量的缩点和割边。
1 #include <cstdio> 2 #include <stack> 3 4 const int N = 5010, M = 10010; 5 6 struct Edge { 7 int nex, v; 8 bool cut; 9 }ecc_edge[M << 1], edge[M << 1]; int tp = 1, ecc_tp; 10 11 int e[N], low[N], dfn[N], num, fr[N], ecc_cnt, in[N], ecc_e[N], vis[N], leaf; 12 std::stack<int> S; 13 14 inline void ecc_add(int x, int y) { 15 ecc_tp++; 16 ecc_edge[ecc_tp].v = y; 17 ecc_edge[ecc_tp].nex = ecc_e[x]; 18 ecc_e[x] = ecc_tp; 19 return; 20 } 21 22 inline void add(int x, int y) { 23 tp++; 24 edge[tp].v = y; 25 edge[tp].nex = e[x]; 26 e[x] = tp; 27 return; 28 } 29 30 void DFS(int x, int f) { 31 leaf += in[x] == 1; 32 vis[x] = 1; 33 for(int i = ecc_e[x]; i; i = ecc_edge[i].nex) { 34 int y = ecc_edge[i].v; 35 if(y != f) { 36 DFS(y, x); 37 } 38 } 39 return; 40 } 41 42 void tarjan(int x, int in_edge) { 43 low[x] = dfn[x] = ++num; 44 S.push(x); 45 for(int i = e[x]; i; i = edge[i].nex) { 46 int y = edge[i].v; 47 if(!dfn[y]) { 48 tarjan(y, i ^ 1); 49 low[x] = std::min(low[x], low[y]); 50 if(low[y] > dfn[x]) { 51 edge[i].cut = edge[i ^ 1].cut = 1; 52 } 53 } 54 else if(i != in_edge) { 55 low[x] = std::min(low[x], dfn[y]); 56 } 57 } 58 if(low[x] == dfn[x]) { 59 int y; 60 ecc_cnt++; 61 do { 62 y = S.top(); 63 S.pop(); 64 fr[y] = ecc_cnt; 65 } while(y != x); 66 } 67 return; 68 } 69 70 int main() { 71 72 int n, m; 73 scanf("%d%d", &n, &m); 74 for(int i = 1, x, y; i <= m; i++) { 75 scanf("%d%d", &x, &y); 76 add(x, y); add(y, x); 77 } 78 79 for(int i = 1; i <= n; i++) { 80 if(!dfn[i]) { 81 tarjan(i, 0); 82 } 83 } 84 85 for(int i = 2; i <= tp; i += 2) { 86 int x = edge[i].v, y = edge[i ^ 1].v; 87 if(fr[x] == fr[y]) { 88 continue; 89 } 90 in[fr[x]]++; 91 in[fr[y]]++; 92 ecc_add(fr[x], fr[y]); 93 ecc_add(fr[y], fr[x]); 94 } 95 96 int t = 0, ans = 0; 97 for(int i = 1; i <= ecc_cnt; i++) { 98 if(!vis[i]) { 99 t++; 100 leaf = 0; 101 DFS(i, 0); 102 ans += (leaf + 1) / 2; 103 } 104 } 105 106 printf("%d\n", ans + (t > 1) * t); 107 return 0; 108 }
然后是点双连通分量的缩点和割点。
1 #include <cstdio> 2 #include <algorithm> 3 4 const int N = 20010, M = 100010; 5 6 struct Edge { 7 int nex, v; 8 }edge[M << 1]; int tp = 1; 9 10 int e[N], dfn[N], low[N], num, ans, cut[N]; 11 12 inline void add(int x, int y) { 13 tp++; 14 edge[tp].v = y; 15 edge[tp].nex = e[x]; 16 e[x] = tp; 17 return; 18 } 19 20 inline void tarjan(int x, int in_edge) { 21 low[x] = dfn[x] = ++num; 22 int cnt = 0; 23 for(int i = e[x]; i; i = edge[i].nex) { 24 int y = edge[i].v; 25 if(!dfn[y]) { 26 tarjan(y, i ^ 1); 27 low[x] = std::min(low[x], low[y]); 28 if(low[y] >= dfn[x]) { /// error : > 29 cnt++; 30 } 31 } 32 else if(i != in_edge) { 33 low[x] = std::min(low[x], dfn[y]); 34 } 35 } 36 if(cnt > 1 || (cnt == 1 && in_edge)) { 37 cut[x] = 1; 38 ans++; 39 } 40 return; 41 } 42 43 int main() { 44 int n, m; 45 scanf("%d%d", &n, &m); 46 for(int i = 1, x, y; i <= m; i++) { 47 scanf("%d%d", &x, &y); 48 add(x, y); add(y, x); 49 } 50 51 for(int i = 1; i <= n; i++) { 52 if(!dfn[i]) { 53 tarjan(i, 0); 54 } 55 } 56 57 printf("%d\n", ans); 58 for(int i = 1; i <= n; i++) { 59 if(cut[i]) { 60 printf("%d ", i); 61 } 62 } 63 64 return 0; 65 }