图连通性问题

Tarjan

众所周知,Tarjan是毒瘤...先%为敬。%%%%%%%%%%%%

有这么一些毒瘤:

有向图求强连通分量并缩点。

无向图求点割点并缩点。

无向图求割边并缩点。

1.有向图求强连通分量并缩点。

这.....我又调了两天才搞出板子来。

洛谷P3387

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <queue>
  4 const int N = 10010, M = 100010;
  5 
  6 struct Edge {
  7     int u, v, nex;
  8 }edge[M]; int top;
  9 int e[N], dfn[N], low[N], num;
 10 int stk[N], t, val[N];
 11 bool in_stk[N];
 12 
 13 Edge scc_edge[M];
 14 int scc_e[N], scc_top, scc_in[N], scc_cnt, scc_val[N];
 15 int topo[N], fr[N];
 16 
 17 inline void add(int x, int y) {
 18     ++top;
 19     edge[top].u = x;
 20     edge[top].v = y;
 21     edge[top].nex = e[x];
 22     e[x] = top;
 23     return;
 24 }
 25 
 26 inline void scc_add(int x, int y) {
 27     scc_top++;
 28     scc_edge[scc_top].v = y;
 29     scc_edge[scc_top].nex = scc_e[x];
 30     scc_e[x] = scc_top;
 31     scc_in[y]++;
 32     return;
 33 }
 34 
 35 void tarjan(int x) {
 36     dfn[x] = low[x] = ++num;
 37     stk[++t] = x;
 38     in_stk[x] = 1;
 39     for(int i = e[x]; i; i = edge[i].nex) {
 40         int y = edge[i].v;
 41         if(!dfn[y]) {
 42             tarjan(y);
 43             low[x] = std::min(low[x], low[y]);
 44         }
 45         else if(in_stk[y]) {
 46             low[x] = std::min(low[x], dfn[y]);
 47         }
 48     }
 49     if(dfn[x] == low[x]) {
 50         scc_cnt++;
 51         int y;
 52         do {
 53             y = stk[t--];
 54             in_stk[y] = 0;
 55             fr[y] = scc_cnt;
 56             scc_val[scc_cnt] += val[y];
 57         }while(y != x);
 58     }
 59     return;
 60 }
 61 
 62 inline void topo_sort() {
 63     std::queue<int> Q;
 64     int temp = 0;
 65     for(int i = 1; i <= scc_cnt; i++) {
 66         if(!scc_in[i]) {
 67             Q.push(i); /// error : topo[++num] = i;
 68         }
 69     }
 70     while(!Q.empty()) {
 71         int x = Q.front();
 72         Q.pop();
 73         topo[++temp] = x;
 74         for(int i = scc_e[x]; i; i = scc_edge[i].nex) {
 75             int y = scc_edge[i].v;
 76             scc_in[y]--;
 77             if(!scc_in[y]) {
 78                 Q.push(y);
 79             }
 80         }
 81     }
 82     return;
 83 }
 84 
 85 int main() {
 86     int n, m;
 87     scanf("%d%d", &n, &m);
 88     for(int i = 1; i <= n; i++) {
 89         scanf("%d", &val[i]);
 90     }
 91     for(int i = 1, x, y; i <= m; i++) {
 92         scanf("%d%d", &x, &y);
 93         if(x == y) {
 94             continue; /// error : space
 95         }
 96         add(x, y);
 97     }
 98     for(int i = 1; i <= n; i++) {
 99         if(!dfn[i]) {
100             tarjan(i);
101         }
102     }
103 
104     for(int i = 1; i <= top; i++) {
105         int x = edge[i].u;
106         int y = edge[i].v;
107         if(fr[x] != fr[y]) {
108             scc_add(fr[x], fr[y]);
109         }
110     }
111     int ans = 0;
112     topo_sort();
113     for(int i = scc_cnt; i; i--) {
114         int x = topo[i];
115         int temp = 0;
116         for(int j = scc_e[x]; j; j = scc_edge[j].nex) {
117             int y = scc_edge[j].v;
118             temp = std::max(temp, scc_val[y]);
119         }
120         scc_val[x] += temp;
121         ans = std::max(ans, scc_val[x]);
122     }
123     printf("%d\n", ans);
124     return 0;
125 }
scc缩点

记得开栈。


洛谷P1726 上白泽慧音

tarjan scc已经打得比较熟了。

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <vector>
 4 
 5 const int N = 5010, M = 50010;
 6 
 7 struct Edge {
 8     int v, nex;
 9 }edge[M << 1]; int top;
10 int e[N], low[N], dfn[N], num;
11 int stk[N], t, scc_cnt;
12 bool in_stk[N];
13 std::vector<int> scc[N];
14 
15 void tarjan(int x) {
16     dfn[x] = low[x] = ++num;
17     stk[++t] = x;
18     in_stk[x] = 1;
19     for(int i = e[x]; i; i = edge[i].nex) {
20         int y = edge[i].v;
21         if(!dfn[y]) {
22             tarjan(y);
23             low[x] = std::min(low[x], low[y]);
24         }
25         else if(in_stk[y]) {
26             low[x] = std::min(low[x], dfn[y]);
27         }
28     }
29     if(low[x] == dfn[x]) {
30         scc_cnt++;
31         int y;
32         do {
33             y = stk[t--];
34             in_stk[y] = 0;
35             scc[scc_cnt].push_back(y);
36         }while(x != y);
37     }
38     return;
39 }
40 
41 inline void add(int x, int y) {
42     ++top;
43     edge[top].v = y;
44     edge[top].nex = e[x];
45     e[x] = top;
46     return;
47 }
48 
49 inline bool great(std::vector<int> &x, std::vector<int> &y) {
50     if(x.size() != y.size()) {
51         return x.size() > y.size();
52     }
53     std::sort(x.begin(), x.end());
54     std::sort(y.begin(), y.end());
55     return x[0] > y[0];
56 }
57 
58 int main() {
59     int m, n;
60     scanf("%d%d", &n ,&m);
61     for(int i = 1, x, y, f; i <= m; i++) {
62         scanf("%d%d%d", &x, &y, &f);
63         add(x, y);
64         if(f == 2) {
65             add(y, x);
66         }
67     }
68     for(int i = 1; i <= n; i++) {
69         if(!dfn[i]) {
70             tarjan(i);
71         }
72     }
73     int ans = 1;
74     for(int i = 1; i <= scc_cnt; i++) {
75         if(great(scc[i], scc[ans])) {
76             ans = i;
77         }
78     }
79     printf("%d\n", scc[ans].size());
80     std::sort(scc[ans].begin(), scc[ans].end());
81     for(int i = 0; i < scc[ans].size(); i++) {
82         printf("%d ", scc[ans][i]);
83     }
84     return 0;
85 }
AC代码

2.无向图求割点

洛谷P3388

 1 #include <cstdio>
 2 #include <algorithm>
 3 const int N = 100010;
 4 
 5 struct Edge {
 6     int v, nex;
 7 }edge[N << 1];int top;
 8 int e[N], low[N], dfn[N], num, root, ans;
 9 bool cut[N];
10 
11 inline void add(int x, int y) {
12     top++;
13     edge[top].v = y;
14     edge[top].nex = e[x];
15     e[x] = top;
16     return;
17 }
18 
19 void tarjan(int x) {
20     dfn[x] = low[x] = ++num;
21     int t = 0;
22     for(int i = e[x]; i; i = edge[i].nex) {
23         int y = edge[i].v;
24         if(!dfn[y]) {
25             tarjan(y);
26             low[x] = std::min(low[x], low[y]);
27             if(dfn[x] <= low[y]) { /// error : > >=
28                 t++;
29             }
30         }
31         else {
32             low[x] = std::min(low[x], dfn[y]);
33         }
34     }
35     if(t > 1 || (t == 1 && x != root)) {
36         cut[x] = 1;
37         ans++;
38     }
39     return;
40 }
41 
42 int main() {
43     int m, n;
44     scanf("%d%d", &n, &m);
45     for(int i = 1, x, y; i <= m; i++) {
46         scanf("%d%d", &x, &y);
47         add(x, y);
48         add(y, x);
49     }
50     for(int x = 1; x <= n; x++) {
51         if(!dfn[x]) {
52             root = x;
53             tarjan(x);
54         }
55     }
56     printf("%d\n", ans);
57     for(int i = 1; i <= n; i++) {
58         if(cut[i]) {
59             printf("%d ", i);
60         }
61     }
62     return 0;
63 }
AC代码

记得对根特判。


3.2_SAT

就是给你n个变量,每个有两种取值,有m个约束条件,形如:

如果x选第一种取值那么y要选第二种。

可知逆否命题也要被满足。

我们把每个变量的两个取值抽象成点,把约束条件连成有向边。

如果有某个变量的两个取值在同一个SCC里,GG。

输出方案数,很多人都是拓扑排序做的。其实有一种很easy的做法:看所属scc的编号。

我们主要是为了避免这种问题:u -> u'

这种情况下只能选u',而不能选u。

通过tarjan的构造方式可知,u'所属的scc一定比u所属的scc先找到。

所以我们对于一个变量,用scc编号较小的那个取值即可。

判断有无解的模板题:

  1 #include <cstdio>
  2 #include <queue>
  3 #include <algorithm>
  4 #include <cstring>
  5 
  6 const int N = 210, M = 1010;
  7 
  8 struct Edge {
  9     int nex, v;
 10 }edge[M << 1]; int top;
 11 
 12 int e[N], dfn[N], low[N], tot, stk[N], t, n;
 13 bool in_stk[N];
 14 char s[10], w[10];
 15 
 16 int scc_cnt, fr[N];
 17 
 18 int chose[N];
 19 
 20 inline bool check() {
 21     for(int i = 1; i <= n; i++) {
 22         if(fr[i] == fr[i + n]) {
 23             return 0;
 24         }
 25     }
 26     return 1;
 27 }
 28 
 29 inline void add(int x, int y) {
 30     top++;
 31     edge[top].v = y;
 32     edge[top].nex = e[x];
 33     e[x] = top;
 34     return;
 35 }
 36 
 37 void tarjan(int x) {
 38     dfn[x] = low[x] = ++tot;
 39     in_stk[x] = 1;
 40     stk[++t] = x;
 41     for(int i = e[x]; i; i = edge[i].nex) {
 42         int y = edge[i].v;
 43         if(!dfn[y]) {
 44             tarjan(y);
 45             low[x] = std::min(low[x], low[y]);
 46         }
 47         else if(in_stk[y]) {
 48             low[x] = std::min(low[x], dfn[y]);
 49         }
 50     }
 51     if(low[x] == dfn[x]) {
 52         scc_cnt++;
 53         int y;
 54         do {
 55             y = stk[t--];
 56             fr[y] = scc_cnt;
 57             in_stk[y] = 0;
 58         } while(y != x);
 59     }
 60     return;
 61 }
 62 
 63 inline void clear() {
 64     top = 0;
 65     tot = 0;
 66     scc_cnt = 0;
 67     memset(e, 0, sizeof(e));
 68     memset(dfn, 0, sizeof(dfn));
 69     memset(low, 0, sizeof(low));
 70     memset(chose, 0, sizeof(chose));
 71     return;
 72 }
 73 
 74 inline int read() {
 75     char c = getchar();
 76     while(c != 'h' && c != 'm') {
 77         c = getchar();
 78     }
 79     int x;
 80     scanf("%d", &x);
 81     if(c == 'm') {
 82         x += n;
 83     }
 84     return x;
 85 }
 86 
 87 inline int _(int x) { // opposite
 88     if(x > n) {
 89         return x - n;
 90     }
 91     return x + n;
 92 }
 93 
 94 int main() {
 95     int T;
 96     scanf("%d", &T);
 97     while(T--) {
 98         int m;
 99         scanf("%d%d", &n, &m);
100         for(int i = 1; i <= m; i++) {
101             int x = read();
102             int y = read();
103             add(_(y), x);
104             add(_(x), y);
105         }
106 
107         for(int i = 1; i <= (n << 1); i++) {
108             if(!dfn[i]) {
109                 tarjan(i);
110             }
111         }
112 
113         if(check()) {
114             printf("GOOD\n");
115         }
116         else {
117             printf("BAD\n");
118         }
119         clear();
120     }
121 
122     return 0;
123 }
洛谷P4171 AC代码

输出方案的(非)模板题:NOI2017 游戏

Naive方法水过的模板题:

 

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 const int N = 1000010;
 5 
 6 struct Edge {
 7     int nex, v;
 8 }edge[N << 1]; int top;
 9 
10 int e[N << 1], stk[N << 1], t, n;
11 bool vis[N << 1];
12 
13 inline void add(int x, int y) {
14     top++;
15     edge[top].v = y;
16     edge[top].nex = e[x];
17     e[x] = top;
18     return;
19 }
20 
21 inline int _(int x) {
22     return x > n ? x - n : x + n;
23 }
24 
25 bool DFS(int x) {
26     if(vis[_(x)]) {
27         return 0;
28     }
29     if(vis[x]) {
30         return 1;
31     }
32     stk[++t] = x;
33     vis[x] = 1;
34     for(int i = e[x]; i; i = edge[i].nex) {
35         int y = edge[i].v;
36         if(!DFS(y)) {
37             return 0;
38         }
39     }
40     return 1;
41 }
42 
43 int main() {
44     int m;
45     scanf("%d%d", &n, &m);
46     for(int i = 1, x, y, z, w; i <= m; i++) {
47         scanf("%d%d%d%d", &x, &y, &z, &w);
48         add(x + (y ^ 1) * n, z + w * n);
49         add(z + (w ^ 1) * n, x + y * n);
50     }
51 
52     for(int i = 1; i <= n; i++) {
53         if(!vis[i] && !vis[i + n]) {
54             t = 0;
55             if(!DFS(i)) {
56                 while(t) {
57                     vis[stk[t]] = 0;
58                     t--;
59                 }
60                 if(!DFS(i + n)) {
61                     printf("IMPOSSIBLE");
62                     return 0;
63                 }
64             }
65         }
66     }
67     printf("POSSIBLE\n");
68     for(int i = 1; i <= n; i++) {
69         printf("%d ", vis[i + n]);
70     }
71     return 0;
72 }
洛谷P4782 AC代码

 

posted @ 2018-07-21 17:14  garage  阅读(236)  评论(0编辑  收藏  举报