2017 01 05 校内网络流小测
鉴于这几日大家都有学习网络流知识,LincHpin大爷决定考一考我们网络流,然后,我就爆零了,233……
T1 Seal
想了好久,也不知道怎么建图,然后心态就崩了,就刷BZOJ去了……
考场上写的随机化和错误贪心都被大爷卡了,T_T,Orz YYH,YZY,GZZ三位AK大佬。
首先,以一个恶魔为中心,形成一个直角的充要条件是,在左右相邻格子中选一个,在上下相邻格子中选一个。
然后发现,对于那些水晶格,同为奇数行的总是不会同时被选,同为偶数行的也不会同时被选(此指被选中流向同一个恶魔格子)。就是说,水晶格可以按照所在行(或者列,随便啦)的奇偶进行黑白染色,每个恶魔总是需要匹配一个黑水晶和一个白水晶。
所以把黑水晶放在左侧,每个和源点之间有容量为1的边;白水晶放在右侧,每个和汇点之间也有容量为1的边。中间是恶魔点,每个恶魔拆成两个点,分别和相邻的黑、白水晶相连,中间是容量为1的边。跑最大流就行了。
1 #include <cstdio> 2 3 inline int nextChar(void) { 4 const int siz = 1024; 5 6 static char buf[siz]; 7 static char *hd = buf + siz; 8 static char *tl = buf + siz; 9 10 if (hd == tl) 11 fread(hd = buf, 1, siz, stdin); 12 13 return *hd++; 14 } 15 16 inline int nextInt(void) { 17 register int ret = 0; 18 register int neg = false; 19 register int bit = nextChar(); 20 21 for (; bit < 48; bit = nextChar()) 22 if (bit == '-')neg ^= true; 23 24 for (; bit > 47; bit = nextChar()) 25 ret = ret * 10 + bit - 48; 26 27 return neg ? -ret : ret; 28 } 29 30 int n, m; 31 32 bool g[55][55]; 33 34 inline bool next(void) 35 { 36 char c = nextChar(); 37 38 while (c != '.' && c != 'X') 39 c = nextChar(); 40 41 return c == '.'; 42 } 43 44 const int siz = 500005; 45 const int inf = 1000000007; 46 47 int tot; 48 int s, t; 49 int hd[siz]; 50 int to[siz]; 51 int fl[siz]; 52 int nt[siz]; 53 54 inline void add(int u, int v, int f) 55 { 56 nt[tot] = hd[u]; to[tot] = v; fl[tot] = f; hd[u] = tot++; 57 nt[tot] = hd[v]; to[tot] = u; fl[tot] = 0; hd[v] = tot++; 58 } 59 60 int dep[siz]; 61 62 inline bool bfs(void) 63 { 64 static int que[siz], head, tail; 65 66 for (int i = s; i <= t; ++i)dep[i] = 0; 67 68 dep[que[head = 0] = s] = tail = 1; 69 70 while (head != tail) 71 { 72 int u = que[head++], v; 73 74 for (int i = hd[u]; ~i; i = nt[i]) 75 if (!dep[v = to[i]] && fl[i]) 76 dep[que[tail++] = v] = dep[u] + 1; 77 } 78 79 return dep[t]; 80 } 81 82 int cur[siz]; 83 84 inline int min(int a, int b) 85 { 86 return a < b ? a : b; 87 } 88 89 int dfs(int u, int f) 90 { 91 if (u == t || !f) 92 return f; 93 94 int used = 0, flow, v; 95 96 for (int i = cur[u]; ~i; i = nt[i]) 97 if (dep[v = to[i]] == dep[u] + 1 && fl[i]) 98 { 99 flow = dfs(v, min(fl[i], f - used)); 100 101 used += flow; 102 fl[i] -= flow; 103 fl[i^1] += flow; 104 105 if (used == f) 106 return f; 107 108 if (fl[i]) 109 cur[u] = i; 110 } 111 112 if (!used) 113 dep[u] = 0; 114 115 return used; 116 } 117 118 inline int maxFlow(void) 119 { 120 int maxFlow = 0, newFlow; 121 122 while (bfs()) 123 { 124 for (int i = s; i <= t; ++i) 125 cur[i] = hd[i]; 126 127 while (newFlow = dfs(s, inf)) 128 maxFlow += newFlow; 129 } 130 131 return maxFlow; 132 } 133 134 inline int pos(int x, int y) 135 { 136 return m * (x - 1) + y; 137 } 138 139 const int mv[4][2] = 140 { 141 {0, +1}, 142 {0, -1}, 143 {-1, 0}, 144 {+1, 0} 145 }; 146 147 signed main(void) 148 { 149 freopen("Seal.in", "r", stdin); 150 freopen("Seal.out", "w", stdout); 151 152 n = nextInt(); 153 m = nextInt(); 154 155 156 for (int i = 1; i <= n; ++i) 157 for (int j = 1; j <= m; ++j) 158 g[i][j] = next(); 159 160 s = 0, t = n * m * 2 + 1; 161 162 for (int i = s; i <= t; ++i) 163 hd[i] = -1; 164 165 for (int i = 1; i <= n; ++i) 166 for (int j = 1; j <= m; ++j) 167 if (g[i][j]) 168 { 169 if ((i + j) & 1) 170 { 171 if (i & 1) 172 add(s, pos(i, j), 1); 173 else 174 add(pos(i, j), t, 1); 175 } 176 else 177 { 178 for (int k = 0; k < 4; ++k) 179 { 180 int x = i + mv[k][0]; 181 int y = j + mv[k][1]; 182 183 if (x < 1 || x > n) 184 continue; 185 if (y < 1 || y > m) 186 continue; 187 if (!g[x][y]) 188 continue; 189 190 if (x & 1) 191 add(pos(x, y), pos(i, j), 1); 192 else 193 add(pos(i, j) + n*m, pos(x, y), 1); 194 195 add(pos(i, j), pos(i, j) + n*m, 1); 196 } 197 } 198 } 199 200 printf("%d\n", maxFlow()); 201 202 return fclose(stdin), fclose(stdout), 0; 203 }
T2 Repair
依旧不会做,然后听LH大爷讲题解,讲完就怀疑自己智商了……
有点像是可行流问题(本来就是可行流好嘛),只是需要分类讨论一下。
对于一条边,如果一开始流量$F$大于$C$,即$F \gt C$,那么有如下情况:
如果最后流量$X$大于$F$,即$X \gt F \gt C$,那么有$fare=X-F+X-C$,如果设$K=F-C$,有$fare=K+2(X-F)$.
如果最后流量$X$在$F$和$C$之间,即$F \gt X \gt C$,那么有$fare=F-X+X-C=K$.
如果最后流量$X$小于$C$,即$F \gt C \gt X$,那么有$fare=F-X=K+C-X$.
不难总结出对于这类边的建新边方式:
先将$K$直接统计到答案之中,然后从$u$向$v$连容量为$INF$的边,费用为$2$,从$v$向$u$连容量为$F-C$的边,费用为$0$,从$v$向$u$连容量为$C$的边,费用为$1$。
对于一条边,如果一开始流量$F$小于$C$,即$F \lt C$,那么有如下情况:
如果最后流量$X$大于$C$,即$X \gt C \gt F$,那么有$fare=X-C+X-F$。
如果最后流量$X$在$C$和$F$之间,即$C \gt X \gt F$,那么有$fare=X-F$。
如果最后流量$X$小于$F$,即$C \gt F \gt X$,那么有$fare=F-X$。
不难总结出对这类边的建新边方式:
从$u$向$v$连容量为$C-F$的边,费用为$1$,从$u$向$v$连容量为$INF$的边,费用为$2$,从$v$向$u$连容量为$F$的边,费用为$1$。
最后为了满足流量守恒,设立新的源汇点,源点向流入量多的点连边,流出量多的点向汇点连边,原汇点向原源点连无穷的边,跑最小费用最大流即可。
1 #include <cstdio> 2 3 inline int nextChar(void) { 4 const int siz = 1024; 5 6 static char buf[siz]; 7 static char *hd = buf + siz; 8 static char *tl = buf + siz; 9 10 if (hd == tl) 11 fread(hd = buf, 1, siz, stdin); 12 13 return *hd++; 14 } 15 16 inline int nextInt(void) { 17 register int ret = 0; 18 register int neg = false; 19 register int bit = nextChar(); 20 21 for (; bit < 48; bit = nextChar()) 22 if (bit == '-')neg ^= true; 23 24 for (; bit > 47; bit = nextChar()) 25 ret = ret * 10 + bit - 48; 26 27 return neg ? -ret : ret; 28 } 29 30 const int siz = 5000005; 31 const int inf = 1000000007; 32 33 int n, m; 34 int answer; 35 int del[siz]; 36 37 int tot; 38 int s, t; 39 int hd[siz]; 40 int to[siz]; 41 int fl[siz]; 42 int vl[siz]; 43 int nt[siz]; 44 45 inline void add(int u, int v, int f, int w) 46 { 47 // printf("add %d %d %d %d\n", u, v, f, w); 48 nt[tot] = hd[u]; to[tot] = v; fl[tot] = f; vl[tot] = +w; hd[u] = tot++; 49 nt[tot] = hd[v]; to[tot] = u; fl[tot] = 0; vl[tot] = -w; hd[v] = tot++; 50 } 51 52 int dep[siz]; 53 54 inline bool bfs(void) 55 { 56 static int que[siz], head, tail; 57 58 for (int i = s; i <= t; ++i)dep[i] = 0; 59 60 dep[que[head = 0] = s] = tail = 1; 61 62 while (head != tail) 63 { 64 int u = que[head++], v; 65 66 for (int i = hd[u]; ~i; i = nt[i]) 67 if (!dep[v = to[i]] && fl[i] && !vl[i]) 68 dep[que[tail++] = v] = dep[u] + 1; 69 } 70 71 return dep[t]; 72 } 73 74 int cur[siz]; 75 76 inline int min(int a, int b) 77 { 78 return a < b ? a : b; 79 } 80 81 int dfs(int u, int f) 82 { 83 if (u == t || !f) 84 return f; 85 86 int used = 0, flow, v; 87 88 for (int i = hd[u]; ~i; i = nt[i]) 89 if (dep[v = to[i]] == dep[u] + 1 && fl[i] && !vl[i]) 90 { 91 flow = dfs(v, min(fl[i], f - used)); 92 93 used += flow; 94 fl[i] -= flow; 95 fl[i^1] += flow; 96 97 if (used == f) 98 return f; 99 100 if (fl[i]) 101 cur[u] = i; 102 } 103 104 if (!used) 105 dep[u] = 0; 106 107 return used; 108 } 109 110 inline void maxFlow(void) 111 { 112 while (bfs()) 113 while (dfs(s, inf)); 114 } 115 116 int dis[siz]; 117 int pre[siz]; 118 119 inline bool spfa(void) 120 { 121 static int que[siz]; 122 static int inq[siz]; 123 static int head, tail; 124 125 for (int i = s; i <= t; ++i) 126 inq[i] = 0, dis[i] = inf; 127 128 head = 0, tail = 0; 129 que[tail++] = s; 130 pre[s] = -1; 131 dis[s] = 0; 132 133 while (head != tail) 134 { 135 int u = que[head++], v; inq[u] = 0; 136 137 for (int i = hd[u]; ~i; i = nt[i]) 138 if (dis[v = to[i]] > dis[u] + vl[i] && fl[i]) 139 { 140 dis[v] = dis[u] + vl[i], pre[v] = i ^ 1; 141 if (!inq[v]) 142 inq[que[tail++] = v] = 1; 143 } 144 } 145 146 return dis[t] < inf; 147 } 148 149 inline void minCost(void) 150 { 151 while (spfa()) 152 { 153 int flow = inf; 154 155 for (int i = pre[t]; ~i; i = pre[to[i]]) 156 if (fl[i ^ 1] < flow)flow = fl[i ^ 1]; 157 158 for (int i = pre[t]; ~i; i = pre[to[i]]) 159 fl[i] += flow, fl[i ^ 1] -= flow; 160 161 answer += dis[t] * flow; 162 } 163 } 164 165 signed main(void) 166 { 167 freopen("Repair.in", "r", stdin); 168 freopen("Repair.out", "w", stdout); 169 170 n = nextInt(); 171 m = nextInt(); 172 173 s = 0, t = n + 1; 174 175 for (int i = s; i <= t; ++i) 176 hd[i] = -1; 177 178 for (int i = 1; i <= m; ++i) 179 { 180 int u = nextInt(); 181 int v = nextInt(); 182 int c = nextInt(); 183 int f = nextInt(); 184 185 del[u] += f; 186 del[v] -= f; 187 188 if (f <= c) 189 { 190 add(u, v, c - f, 1); 191 add(u, v, inf, 2); 192 add(v, u, f, 1); 193 } 194 else 195 { 196 answer += f - c; 197 add(u, v, inf, 2); 198 add(v, u, f - c, 0); 199 add(v, u, c, 1); 200 } 201 } 202 203 add(n, 1, inf, 0); 204 205 for (int i = 1; i <= n; ++i) 206 if (del[i] < 0) 207 add(s, i, -del[i], 0); 208 else if (del[i] > 0) 209 add(i, t, del[i], 0); 210 211 maxFlow(); 212 213 minCost(); 214 215 printf("%d\n", answer); 216 217 return fclose(stdin), fclose(stdout), 0; 218 }
T3 Flow
依旧不会,看到$G(f)$函数就是mengbi的。
GZZ神犇说,MCMF中每次增广出来的路径,就是$C(f)$关于$F(f)$函数中的一段一次函数…… $OTZ GZZ$
然后,分步跑出来每一段的函数,求函数上距离(maxFlow,0)点的最近距离即可,我怎么这么蠢,23333
1 #include <cstdio> 2 3 template <class T> 4 inline T min(T a, T b) 5 { 6 return a < b ? a : b; 7 } 8 9 template <class T> 10 T gcd(T a, T b) 11 { 12 return b ? gcd(b, a % b) : a; 13 } 14 15 template <class T> 16 inline T sqr(T x) 17 { 18 return x * x; 19 } 20 21 #define int long long 22 23 inline int nextChar(void) { 24 const int siz = 1024; 25 26 static char buf[siz]; 27 static char *hd = buf + siz; 28 static char *tl = buf + siz; 29 30 if (hd == tl) 31 fread(hd = buf, 1, siz, stdin); 32 33 return *hd++; 34 } 35 36 inline int nextInt(void) { 37 register int ret = 0; 38 register int neg = false; 39 register int bit = nextChar(); 40 41 for (; bit < 48; bit = nextChar()) 42 if (bit == '-')neg ^= true; 43 44 for (; bit > 47; bit = nextChar()) 45 ret = ret * 10 + bit - 48; 46 47 return neg ? -ret : ret; 48 } 49 50 const int siz = 500005; 51 const int inf = 1000000007; 52 53 int n, m; 54 55 int tot; 56 int s, t; 57 int hd[siz]; 58 int nt[siz]; 59 int fl[siz]; 60 int vl[siz]; 61 int to[siz]; 62 63 inline void add(int u, int v, int f, int w) 64 { 65 nt[tot] = hd[u]; to[tot] = v; vl[tot] = +w; fl[tot] = f; hd[u] = tot++; 66 nt[tot] = hd[v]; to[tot] = u; vl[tot] = -w; fl[tot] = 0; hd[v] = tot++; 67 } 68 69 int dep[siz]; 70 71 inline bool bfs(void) 72 { 73 static int que[siz], head, tail; 74 75 for (int i = 1; i <= n; ++i)dep[i] = 0; 76 77 dep[que[head = 0] = s] = tail = 1; 78 79 while (head != tail) 80 { 81 int u = que[head++], v; 82 83 for (int i = hd[u]; ~i; i = nt[i]) 84 if (!dep[v = to[i]] && fl[i]) 85 dep[que[tail++] = v] = dep[u] + 1; 86 } 87 88 return dep[t]; 89 } 90 91 int cur[siz]; 92 93 int dfs(int u, int f) 94 { 95 if (u == t || !f) 96 return f; 97 98 int used = 0, flow, v; 99 100 for (int i = cur[u]; ~i; i = nt[i]) 101 if (dep[v = to[i]] == dep[u] + 1 && fl[i]) 102 { 103 flow = dfs(v, min(fl[i], f - used)); 104 105 used += flow; 106 fl[i] -= flow; 107 fl[i^1] += flow; 108 109 if (used == f) 110 return f; 111 112 if (fl[i]) 113 cur[u] = i; 114 } 115 116 if (!used) 117 dep[u] = 0; 118 119 return used; 120 } 121 122 inline int Dinic(void) 123 { 124 int maxFlow = 0, newFlow; 125 126 while (bfs()) 127 { 128 for (int i = 1; i <= n; ++i) 129 cur[i] = hd[i]; 130 131 while (newFlow = dfs(s, inf)) 132 maxFlow += newFlow; 133 } 134 135 return maxFlow; 136 } 137 138 int dis[siz]; 139 int pre[siz]; 140 141 inline bool spfa(void) 142 { 143 static int que[siz]; 144 static int inq[siz]; 145 static int head, tail; 146 147 for (int i = 1; i <= n; ++i) 148 dis[i] = inf, inq[i] = 0; 149 150 head = 0, tail = 0; 151 que[tail++] = s; 152 pre[s] = -1; 153 dis[s] = 0; 154 inq[s] = 1; 155 156 while (head != tail) 157 { 158 int u = que[head++], v; inq[u] = 0; 159 160 for (int i = hd[u]; ~i; i = nt[i]) 161 if (fl[i] && dis[v = to[i]] > dis[u] + vl[i]) 162 { 163 dis[v] = dis[u] + vl[i], pre[v] = i ^ 1; 164 if (!inq[v])inq[que[tail++] = v] = 1; 165 } 166 } 167 168 return dis[t] < inf; 169 } 170 171 signed main(void) 172 { 173 freopen("Flow.in", "r", stdin); 174 freopen("Flow.out", "w", stdout); 175 176 n = nextInt(); 177 m = nextInt(); 178 s = nextInt(); 179 t = nextInt(); 180 181 for (int i = 1; i <= n; ++i) 182 hd[i] = -1; 183 184 for (int i = 1; i <= m; ++i) 185 { 186 int x = nextInt(); 187 int y = nextInt(); 188 int u = nextInt(); 189 int c = nextInt(); 190 add(x, y, u, c); 191 } 192 193 int maxFlow = Dinic(); 194 195 for (int i = 0; i < tot; i += 2) 196 fl[i] += fl[i ^ 1], fl[i ^ 1] = 0; 197 198 int C = 0, F = maxFlow; 199 200 while (spfa()) 201 { 202 int w = dis[t]; 203 204 int flow = inf; 205 206 for (int i = pre[t]; ~i; i = pre[to[i]]) 207 if (fl[i ^ 1] < flow) 208 flow = fl[i ^ 1]; 209 210 if (F - w * C >= flow * (sqr(w) + 1)) 211 { 212 for (int i = pre[t]; ~i; i = pre[to[i]]) 213 fl[i] += flow, fl[i ^ 1] -= flow; 214 215 F -= flow, C += flow * dis[t]; 216 } 217 else 218 { 219 if (F > w * C) 220 { 221 int r = sqr(w * F + C); 222 int d = sqr(w) + 1; 223 int g = gcd(r, d); 224 printf("%lld/%lld\n", r / g, d / g); 225 } 226 else 227 printf("%lld/%lld\n", sqr(C) + sqr(F), 1LL); 228 229 goto end; 230 } 231 } 232 233 printf("%lld/%lld\n", sqr(C) + sqr(F), 1LL); 234 235 end:return fclose(stdin), fclose(stdout), 0; 236 }
@Author: YouSiki