POI[2011]Garbage 欧拉回路的正确姿势
题目大意是讲给定一张图,每跳边有编号$0$或$1$,每次可以将一个简单环上所有边$\oplus 1$,构造方案使得每跳边的编号是某个值,方案中涉及的边不超过5m
如果方案中的两个环有相交部分,相交部分没有变化,去掉之后2个环合并成了一个环。所以存在一种方案使得所有环都不相交
于是删除需要进过偶数次的边,对每个联通块求欧拉回路(不是道路),然后再用栈搞一搞弄出所有的环,时间复杂度$O(n + m)$
TLE...,为了卡常数还怒写人工栈...然后发现欧拉回路部分,一开始我是这么写的
void dfs(int u) { vis[u] = 1; for (int i = head[u], v; v = e[i].node, i; i = e[i].next) if (!flag[i]) { flag[i] = flag[i ^ 1] = 1; dfs(v); mem[++mem[0]] = i; } }
一直都以为这样写就是对的,然后发现自己太naive了,每个点都从头开始找没有被标记过的边,复杂度是能退化的。。
正确的姿势应该是记录每个点最后一条标记的边是哪一条,然后从那一条边开始继续扫描(人工栈版本)
void dfs(int rt) { S[top = 1].u = rt, S[1].i = head[rt], S[1].t = 0; while (top) { int u = S[top].u, i = S[top].i, tmp = top; S[top].t++; if (S[top].t == 2) {mem[++mem[0]] = i; S[top].t = 1;} i = cur[u]; vis[u] = 1; for (int v; v = e[i].node, i; i = e[i].next) if (!flag[i]) { flag[i] = flag[i ^ 1] = 1; S[++top].u = v; S[top].i = cur[v]; S[top].t = 0; S[tmp].i = i; cur[u] = i; break; } if (tmp == top) top--; } }
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 #include <algorithm> 6 #include <vector> 7 #include <ctime> 8 using namespace std; 9 #define rep(i, l, r) for (int i = l; i <= r; i++) 10 #define drep(i, r, l) for (int i = r; i >= l; i--) 11 typedef long long ll; 12 const int N = 1e5 + 8, M = 1e6 + 8; 13 int n, m, tot, head[N], mem[M << 1], top, sz, cnt[N], q[M << 1], deg[N], vis[N], cur[N]; 14 bool flag[M << 1]; 15 int ans[M * 5], len, sta[M]; 16 int xgw; 17 //double t1, t2; 18 int getint() 19 { 20 char c; int num = 0, w = 1; 21 for (c = getchar(); !isdigit(c) && c != '-'; c = getchar()); 22 if (c == '-') c = getchar(), w = -1; 23 for (;isdigit(c); c = getchar()) num = num * 10 + c - '0'; 24 return num * w; 25 } 26 struct Edge{int next, node;}e[M << 1]; 27 inline void add(int x, int y) 28 { 29 e[++tot].next = head[x], head[x] = tot, e[tot].node = y; 30 e[++tot].next = head[y], head[y] = tot, e[tot].node = x; 31 } 32 struct RGZ 33 { 34 int u, i, t; 35 }S[M]; 36 void dfs(int rt) 37 { 38 S[top = 1].u = rt, S[1].i = head[rt], S[1].t = 0; 39 while (top) 40 { 41 int u = S[top].u, i = S[top].i, tmp = top; S[top].t++; 42 if (S[top].t == 2) {mem[++mem[0]] = i; S[top].t = 1;} 43 i = cur[u]; 44 vis[u] = 1; 45 for (int v; v = e[i].node, i; i = e[i].next) 46 if (!flag[i]) 47 { 48 flag[i] = flag[i ^ 1] = 1; 49 S[++top].u = v; S[top].i = cur[v]; 50 S[top].t = 0; S[tmp].i = i; cur[u] = i; 51 break; 52 } 53 if (tmp == top) top--; 54 } 55 } 56 inline bool calc(int rt) 57 { 58 mem[0] = 0; top = 0; 59 dfs(rt); 60 if (!mem[0]) return 1; 61 q[top = 1] = e[mem[mem[0]] ^ 1].node; cnt[q[1]]++; 62 drep(i, mem[0], 1) 63 { 64 int u = e[mem[i]].node; 65 cnt[u]++; 66 if (cnt[u] == 2) 67 { 68 ++sz; ans[++len] = u; sta[sz] = len; 69 while (cnt[u] > 1) 70 ans[++len] = q[top], cnt[q[top]]--, top--; 71 } 72 q[++top] = u; 73 } 74 if (top > 1) return 0; 75 } 76 void solve() 77 { 78 rep(i, 1, n) 79 if (deg[i] & 1) 80 { 81 printf("NIE\n"); 82 return; 83 } 84 rep(i, 1, n) cur[i] = head[i]; 85 rep(i, 1, n) 86 if (deg[i] && !vis[i]) 87 if (!calc(i)) 88 { 89 printf("NIE\n"); 90 return; 91 } 92 printf("%d\n", sz); sta[sz + 1] = len + 1; 93 rep(i, 1, sz) 94 { 95 printf("%d ", sta[i + 1] - sta[i] - 1); 96 rep(j, sta[i], sta[i + 1] - 1) printf("%d ", ans[j]); 97 putchar('\n'); 98 } 99 } 100 int main() 101 { 102 //freopen("input.txt","r",stdin); 103 //freopen("output.txt","w",stdout); 104 scanf("%d%d", &n, &m); tot = 1; 105 rep(i, 1, m) 106 { 107 int u = getint(), v = getint(), x = getint(), y = getint(); 108 if (y != x) add(u, v), deg[u]++, deg[v]++; 109 } 110 solve(); 111 //fclose(stdin); fclose(stdout); 112 return 0; 113 }