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
", ans);
148     return 0;
149 }
洛谷P3627 抢掠计划

然后是边双连通分量的缩点和割边。

  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
", ans + (t > 1) * t);
107     return 0;
108 }
POJ3177 Redundant Paths

然后是点双连通分量的缩点和割点。

 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
", ans);
58     for(int i = 1; i <= n; i++) {
59         if(cut[i]) {
60             printf("%d ", i);
61         }
62     }
63 
64     return 0;
65 }
洛谷P3388 割点
原文地址:https://www.cnblogs.com/huyufeifei/p/10424342.html