2017 01 05 校内网络流小测
2017-01-05 17:00
183 查看
鉴于这几日大家都有学习网络流知识,LincHpin大爷决定考一考我们网络流,然后,我就爆零了,233……
考场上写的随机化和错误贪心都被大爷卡了,T_T,Orz YYH,YZY,GZZ三位AK大佬。
首先,以一个恶魔为中心,形成一个直角的充要条件是,在左右相邻格子中选一个,在上下相邻格子中选一个。
然后发现,对于那些水晶格,同为奇数行的总是不会同时被选,同为偶数行的也不会同时被选(此指被选中流向同一个恶魔格子)。就是说,水晶格可以按照所在行(或者列,随便啦)的奇偶进行黑白染色,每个恶魔总是需要匹配一个黑水晶和一个白水晶。
所以把黑水晶放在左侧,每个和源点之间有容量为1的边;白水晶放在右侧,每个和汇点之间也有容量为1的边。中间是恶魔点,每个恶魔拆成两个点,分别和相邻的黑、白水晶相连,中间是容量为1的边。跑最大流就行了。
有点像是可行流问题(本来就是可行流好嘛),只是需要分类讨论一下。
对于一条边,如果一开始流量$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$。
最后为了满足流量守恒,设立新的源汇点,源点向流入量多的点连边,流出量多的点向汇点连边,原汇点向原源点连无穷的边,跑最小费用最大流即可。
GZZ神犇说,MCMF中每次增广出来的路径,就是$C(f)$关于$F(f)$函数中的一段一次函数…… $OTZ GZZ$
然后,分步跑出来每一段的函数,求函数上距离(maxFlow,0)点的最近距离即可,我怎么这么蠢,23333
@Author: YouSiki
T1 Seal
想了好久,也不知道怎么建图,然后心态就崩了,就刷BZOJ去了……考场上写的随机化和错误贪心都被大爷卡了,T_T,Orz YYH,YZY,GZZ三位AK大佬。
首先,以一个恶魔为中心,形成一个直角的充要条件是,在左右相邻格子中选一个,在上下相邻格子中选一个。
然后发现,对于那些水晶格,同为奇数行的总是不会同时被选,同为偶数行的也不会同时被选(此指被选中流向同一个恶魔格子)。就是说,水晶格可以按照所在行(或者列,随便啦)的奇偶进行黑白染色,每个恶魔总是需要匹配一个黑水晶和一个白水晶。
所以把黑水晶放在左侧,每个和源点之间有容量为1的边;白水晶放在右侧,每个和汇点之间也有容量为1的边。中间是恶魔点,每个恶魔拆成两个点,分别和相邻的黑、白水晶相连,中间是容量为1的边。跑最大流就行了。
#include <cstdio> inline int nextChar(void) { const int siz = 1024; static char buf[siz]; static char *hd = buf + siz; static char *tl = buf + siz; if (hd == tl) fread(hd = buf, 1, siz, stdin); return *hd++; } inline int nextInt(void) { register int ret = 0; register int neg = false; register int bit = nextChar(); for (; bit < 48; bit = nextChar()) if (bit == '-')neg ^= true; for (; bit > 47; bit = nextChar()) ret = ret * 10 + bit - 48; return neg ? -ret : ret; } int n, m; bool g[55][55]; inline bool next(void) { char c = nextChar(); while (c != '.' && c != 'X') c = nextChar(); return c == '.'; } const int siz = 500005; const int inf = 1000000007; int tot; int s, t; int hd[siz]; int to[siz]; int fl[siz]; int nt[siz]; inline void add(int u, int v, int f) { nt[tot] = hd[u]; to[tot] = v; fl[tot] = f; hd[u] = tot++; nt[tot] = hd[v]; to[tot] = u; fl[tot] = 0; hd[v] = tot++; } int dep[siz]; inline bool bfs(void) { static int que[siz], head, tail; for (int i = s; i <= t; ++i)dep[i] = 0; dep[que[head = 0] = s] = tail = 1; while (head != tail) { int u = que[head++], v; for (int i = hd[u]; ~i; i = nt[i]) if (!dep[v = to[i]] && fl[i]) dep[que[tail++] = v] = dep[u] + 1; } return dep[t]; } int cur[siz]; inline int min(int a, int b) { return a < b ? a : b; } int dfs(int u, int f) { if (u == t || !f) return f; int used = 0, flow, v; for (int i = cur[u]; ~i; i = nt[i]) if (dep[v = to[i]] == dep[u] + 1 && fl[i]) { flow = dfs(v, min(fl[i], f - used)); used += flow; fl[i] -= flow; fl[i^1] += flow; if (used == f) return f; if (fl[i]) cur[u] = i; } if (!used) dep[u] = 0; return used; } inline int maxFlow(void) { int maxFlow = 0, newFlow; while (bfs()) { for (int i = s; i <= t; ++i) cur[i] = hd[i]; while (newFlow = dfs(s, inf)) maxFlow += newFlow; } return maxFlow; } inline int pos(int x, int y) { return m * (x - 1) + y; } const int mv[4][2] = { {0, +1}, {0, -1}, {-1, 0}, {+1, 0} }; signed main(void) { freopen("Seal.in", "r", stdin); freopen("Seal.out", "w", stdout); n = nextInt(); m = nextInt(); for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) g[i][j] = next(); s = 0, t = n * m * 2 + 1; for (int i = s; i <= t; ++i) hd[i] = -1; for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) if (g[i][j]) { if ((i + j) & 1) { if (i & 1) add(s, pos(i, j), 1); else add(pos(i, j), t, 1); } else { for (int k = 0; k < 4; ++k) { int x = i + mv[k][0]; int y = j + mv[k][1]; if (x < 1 || x > n) continue; if (y < 1 || y > m) continue; if (!g[x][y]) continue; if (x & 1) add(pos(x, y), pos(i, j), 1); else add(pos(i, j) + n*m, pos(x, y), 1); add(pos(i, j), pos(i, j) + n*m, 1); } } } printf("%d\n", maxFlow()); return fclose(stdin), fclose(stdout), 0; }
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$。
最后为了满足流量守恒,设立新的源汇点,源点向流入量多的点连边,流出量多的点向汇点连边,原汇点向原源点连无穷的边,跑最小费用最大流即可。
#include <cstdio> inline int nextChar(void) { const int siz = 1024; static char buf[siz]; static char *hd = buf + siz; static char *tl = buf + siz; if (hd == tl) fread(hd = buf, 1, siz, stdin); return *hd++; } inline int nextInt(void) { register int ret = 0; register int neg = false; register int bit = nextChar(); for (; bit < 48; bit = nextChar()) if (bit == '-')neg ^= true; for (; bit > 47; bit = nextChar()) ret = ret * 10 + bit - 48; return neg ? -ret : ret; } const int siz = 5000005; const int inf = 1000000007; int n, m; int answer; int del[siz]; int tot; int s, t; int hd[siz]; int to[siz]; int fl[siz]; int vl[siz]; int nt[siz]; inline void add(int u, int v, int f, int w) { // printf("add %d %d %d %d\n", u, v, f, w); nt[tot] = hd[u]; to[tot] = v; fl[tot] = f; vl[tot] = +w; hd[u] = tot++; nt[tot] = hd[v]; to[tot] = u; fl[tot] = 0; vl[tot] = -w; hd[v] = tot++; } int dep[siz]; inline bool bfs(void) { static int que[siz], head, tail; for (int i = s; i <= t; ++i)dep[i] = 0; dep[que[head = 0] = s] = tail = 1; while (head != tail) { int u = que[head++], v; for (int i = hd[u]; ~i; i = nt[i]) if (!dep[v = to[i]] && fl[i] && !vl[i]) dep[que[tail++] = v] = dep[u] + 1; } return dep[t]; } int cur[siz]; inline int min(int a, int b) { return a < b ? a : b; } int dfs(int u, int f) { if (u == t || !f) return f; int used = 0, flow, v; for (int i = hd[u]; ~i; i = nt[i]) if (dep[v = to[i]] == dep[u] + 1 && fl[i] && !vl[i]) { flow = dfs(v, min(fl[i], f - used)); used += flow; fl[i] -= flow; fl[i^1] += flow; if (used == f) return f; if (fl[i]) cur[u] = i; } if (!used) dep[u] = 0; return used; } inline void maxFlow(void) { while (bfs()) while (dfs(s, inf)); } int dis[siz]; int pre[siz]; inline bool spfa(void) { static int que[siz]; static int inq[siz]; static int head, tail; for (int i = s; i <= t; ++i) inq[i] = 0, dis[i] = inf; head = 0, tail = 0; que[tail++] = s; pre[s] = -1; dis[s] = 0; while (head != tail) { int u = que[head++], v; inq[u] = 0; for (int i = hd[u]; ~i; i = nt[i]) if (dis[v = to[i]] > dis[u] + vl[i] && fl[i]) { dis[v] = dis[u] + vl[i], pre[v] = i ^ 1; if (!inq[v]) inq[que[tail++] = v] = 1; } } return dis[t] < inf; } inline void minCost(void) { while (spfa()) { int flow = inf; for (int i = pre[t]; ~i; i = pre[to[i]]) if (fl[i ^ 1] < flow)flow = fl[i ^ 1]; for (int i = pre[t]; ~i; i = pre[to[i]]) fl[i] += flow, fl[i ^ 1] -= flow; answer += dis[t] * flow; } } signed main(void) { freopen("Repair.in", "r", stdin); freopen("Repair.out", "w", stdout); n = nextInt(); m = nextInt(); s = 0, t = n + 1; for (int i = s; i <= t; ++i) hd[i] = -1; for (int i = 1; i <= m; ++i) { int u = nextInt(); int v = nextInt(); int c = nextInt(); int f = nextInt(); del[u] += f; del[v] -= f; if (f <= c) { add(u, v, c - f, 1); add(u, v, inf, 2); add(v, u, f, 1); } else { answer += f - c; add(u, v, inf, 2); add(v, u, f - c, 0); add(v, u, c, 1); } } add(n, 1, inf, 0); for (int i = 1; i <= n; ++i) if (del[i] < 0) add(s, i, -del[i], 0); else if (del[i] > 0) add(i, t, del[i], 0); maxFlow(); minCost(); printf("%d\n", answer); return fclose(stdin), fclose(stdout), 0; }
T3 Flow
依旧不会,看到$G(f)$函数就是mengbi的。GZZ神犇说,MCMF中每次增广出来的路径,就是$C(f)$关于$F(f)$函数中的一段一次函数…… $OTZ GZZ$
然后,分步跑出来每一段的函数,求函数上距离(maxFlow,0)点的最近距离即可,我怎么这么蠢,23333
#include <cstdio> template <class T> inline T min(T a, T b) { return a < b ? a : b; } template <class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; } template <class T> inline T sqr(T x) { return x * x; } #define int long long inline int nextChar(void) { const int siz = 1024; static char buf[siz]; static char *hd = buf + siz; static char *tl = buf + siz; if (hd == tl) fread(hd = buf, 1, siz, stdin); return *hd++; } inline int nextInt(void) { register int ret = 0; register int neg = false; register int bit = nextChar(); for (; bit < 48; bit = nextChar()) if (bit == '-')neg ^= true; for (; bit > 47; bit = nextChar()) ret = ret * 10 + bit - 48; return neg ? -ret : ret; } const int siz = 500005; const int inf = 1000000007; int n, m; int tot; int s, t; int hd[siz]; int nt[siz]; int fl[siz]; int vl[siz]; int to[siz]; inline void add(int u, int v, int f, int w) { nt[tot] = hd[u]; to[tot] = v; vl[tot] = +w; fl[tot] = f; hd[u] = tot++; nt[tot] = hd[v]; to[tot] = u; vl[tot] = -w; fl[tot] = 0; hd[v] = tot++; } int dep[siz]; inline bool bfs(void) { static int que[siz], head, tail; for (int i = 1; i <= n; ++i)dep[i] = 0; dep[que[head = 0] = s] = tail = 1; while (head != tail) { int u = que[head++], v; for (int i = hd[u]; ~i; i = nt[i]) if (!dep[v = to[i]] && fl[i]) dep[que[tail++] = v] = dep[u] + 1; } return dep[t]; } int cur[siz]; int dfs(int u, int f) { if (u == t || !f) return f; int used = 0, flow, v; for (int i = cur[u]; ~i; i = nt[i]) if (dep[v = to[i]] == dep[u] + 1 && fl[i]) { flow = dfs(v, min(fl[i], f - used)); used += flow; fl[i] -= flow; fl[i^1] += flow; if (used == f) return f; if (fl[i]) cur[u] = i; } if (!used) dep[u] = 0; return used; } inline int Dinic(void) { int maxFlow = 0, newFlow; while (bfs()) { for (int i = 1; i <= n; ++i) cur[i] = hd[i]; while (newFlow = dfs(s, inf)) maxFlow += newFlow; } return maxFlow; } int dis[siz]; int pre[siz]; inline bool spfa(void) { static int que[siz]; static int inq[siz]; static int head, tail; for (int i = 1; i <= n; ++i) dis[i] = inf, inq[i] = 0; head = 0, tail = 0; que[tail++] = s; pre[s] = -1; dis[s] = 0; inq[s] = 1; while (head != tail) { int u = que[head++], v; inq[u] = 0; for (int i = hd[u]; ~i; i = nt[i]) if (fl[i] && dis[v = to[i]] > dis[u] + vl[i]) { dis[v] = dis[u] + vl[i], pre[v] = i ^ 1; if (!inq[v])inq[que[tail++] = v] = 1; } } return dis[t] < inf; } signed main(void) { freopen("Flow.in", "r", stdin); freopen("Flow.out", "w", stdout); n = nextInt(); m = nextInt(); s = nextInt(); t = nextInt(); for (int i = 1; i <= n; ++i) hd[i] = -1; for (int i = 1; i <= m; ++i) { int x = nextInt(); int y = nextInt(); int u = nextInt(); int c = nextInt(); add(x, y, u, c); } int maxFlow = Dinic(); for (int i = 0; i < tot; i += 2) fl[i] += fl[i ^ 1], fl[i ^ 1] = 0; int C = 0, F = maxFlow; while (spfa()) { int w = dis[t]; int flow = inf; for (int i = pre[t]; ~i; i = pre[to[i]]) if (fl[i ^ 1] < flow) flow = fl[i ^ 1]; if (F - w * C >= flow * (sqr(w) + 1)) { for (int i = pre[t]; ~i; i = pre[to[i]]) fl[i] += flow, fl[i ^ 1] -= flow; F -= flow, C += flow * dis[t]; } else { if (F > w * C) { int r = sqr(w * F + C); int d = sqr(w) + 1; int g = gcd(r, d); printf("%lld/%lld\n", r / g, d / g); } else printf("%lld/%lld\n", sqr(C) + sqr(F), 1LL); goto end; } } printf("%lld/%lld\n", sqr(C) + sqr(F), 1LL); end:return fclose(stdin), fclose(stdout), 0; }
@Author: YouSiki
相关文章推荐
- 2017 01 16 校内小测 ZXR专场
- 2017 百度之星复赛题解 01、03、05
- 01_Android应用开发环境_05_签名android应用程序
- 01_05.border属性
- 10-01-05
- 经典C语言程序设计100例 -- C 和 Python 版 (01 - 05)
- 10-05-01
- 10 05 01 QY
- 2017-01-02 上课类容
- 11 01 05 事情做不完
- 2017百度之星初赛(A) 度度熊的01世界(BFS/DFS)
- nodejs一周动态(2011-04-25 - 05-01)
- 2017_12_05 echarts动态赋值问题,tab切换时,图表显示错乱
- 01_JavaMail_05_创建邮件工具类MailUtils等方便发送邮件
- 2017 CCCC 决赛 L1 - 01 二叉搜索树的结构 【二叉搜索树的考察】
- Github全面详解-05课后作业01
- 《网络流学习笔记05--最小割最大流问题》
- 2017_07_05
- MFC-01-Chapter01:Hello,MFC---1.3 第一个MFC程序(05)
- zoj 2676 Network Wars(01分数规划+网络流)