图连通性问题
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 }
记得开栈。
洛谷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 }
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 }
记得对根特判。
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 }
输出方案的(非)模板题: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 }