ZJOI 最小割 CQOI 不同的最小割 (最小割分治)
题目1 ZJOI 最小割
题目大意:
求一个无向带权图两点间的最小割,询问小于等于c的点对有多少。
算法讨论: 最小割 分治
代码:
#include <cstdlib> #include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <vector> using namespace std; const int N = 150 + 5; const int M = 3000 + 5; const int oo = 0x3f3f3f3f; #define inf oo int n, m; bool mark[N]; int a[N], tmp[N], ans[N][N]; struct Edge { int from, to, cap, flow; Edge(int u = 0, int v = 0, int c = 0, int f = 0) : from(u), to(v), cap(c), flow(f) {} }; struct Dinic { int n, mm, s, t; int dis[N], cur[N], que[N * 10]; bool vis[N]; vector <Edge> edges; vector <int> G[N]; void clear() { for(int i = 0; i <= n; ++ i) G[i].clear(); edges.clear(); } void add(int from, int to, int cap) { edges.push_back(Edge(from, to, cap, 0)); edges.push_back(Edge(to, from, cap, 0)); mm = edges.size(); G[from].push_back(mm - 2); G[to].push_back(mm - 1); } bool bfs() { int head = 1, tail = 1; memset(vis, false, (n + 1) * sizeof (bool)); dis[s] = 0; vis[s] = true; que[head] = s; while(head <= tail) { int x = que[head]; for(int i = 0; i < (signed) G[x].size(); ++ i) { Edge &e = edges[G[x][i]]; if(!vis[e.to] && e.cap > e.flow) { vis[e.to] = true; dis[e.to] = dis[x] + 1; que[++ tail] = e.to; } } ++ head; } return vis[t]; } int dfs(int x, int a) { if(x == t || a == 0) return a; int flw = 0, f; for(int &i = cur[x]; i < (signed) G[x].size(); ++ i) { Edge &e = edges[G[x][i]]; if(dis[e.to] == dis[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) { e.flow += f; edges[G[x][i] ^ 1].flow -= f; flw += f; a -= f; if(a == 0) break; } } return flw; } int maxflow(int s, int t) { this->s = s; this->t = t; int flw = 0; while(bfs()) { memset(cur, 0, sizeof cur); flw += dfs(s, oo); } return flw; } void rebuild() { for(int i = 0; i < (signed) edges.size(); ++ i) edges[i].flow = 0; } void dfs(int u) { mark[u] = true; for(int i = 0; i < (signed) G[u].size(); ++ i) { Edge e = edges[G[u][i]]; if(!mark[e.to] && e.cap > e.flow) { dfs(e.to); } } } }net; void Divide(int l, int r) { if(l >= r) return; net.rebuild(); int nowflow = net.maxflow(a[l], a[r]); memset(mark, false, (n + 1) * sizeof (bool)); net.dfs(a[l]); for(int i = 1; i <= n; ++ i) if(mark[i]) for(int j = 1; j <= n; ++ j) if(!mark[j]) ans[i][j] = min(ans[i][j], nowflow), ans[j][i] = ans[i][j]; int L = l, R = r; for(int i = l; i <= r; ++ i) if(mark[a[i]]) tmp[L ++] = a[i]; else tmp[R --] = a[i]; for(int i = l; i <= r; ++ i) a[i] = tmp[i]; Divide(l, L - 1); Divide(R + 1, r);//这不能二分一个Mid,因为S集和T集的大小不一定相同 } int main() { int T, u, v, c, q; scanf("%d", &T); while(T --) { net.clear(); scanf("%d%d", &n, &m); net.n = n; for(int i = 1; i <= n; ++ i) a[i] = i; for(int i = 1; i <= n; ++ i) for(int j = 1; j <= n; ++ j) ans[i][j] = inf; for(int i = 1; i <= m; ++ i) { scanf("%d%d%d", &u, &v, &c); net.add(u, v, c); } Divide(1, n); scanf("%d", &q); for(int i = 1; i <= q; ++ i) { int tmp = 0; scanf("%d", &c); for(int u = 1; u <= n; ++ u) { for(int v = u + 1; v <= n; ++ v) { if(ans[u][v] <= c) { ++ tmp; } } } printf("%d\n", tmp); } puts(""); } return 0; }
题目2 CQOI2016 不同的最小割
题目大意:
求所有点对问不同的最小割数目。
算法讨论: 最小割 分治
和上面的一个题有区别么?
代码:
#include <cstdlib> #include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <vector> #include <set> using namespace std; const int N = 850 + 5; const int M = 8500 + 5; const int oo = 0x3f3f3f3f; int n, m; int ans[N][N], tmp[N], a[N]; bool mark[N]; set <int> lts; struct Edge { int from, to, cap, flow; Edge(int u = 0, int v = 0, int cap = 0, int flow = 0): from(u), to(v), cap(cap), flow(flow) {} }; struct Dinic { int n, m, s, t; int dis[N], cur[N], que[N * 10]; bool vis[N]; vector <Edge> edges; vector <int> G[N]; void add(int from, int to, int cap) { edges.push_back(Edge(from, to, cap, 0)); edges.push_back(Edge(to, from, cap, 0)); m = edges.size(); G[from].push_back(m - 2); G[to].push_back(m - 1); } bool bfs() { int head = 1, tail = 1; memset(vis, false, (n + 1) * sizeof (bool)); dis[s] = 0; vis[s] = true; que[head] = s; while(head <= tail) { int x = que[head]; for(int i = 0; i < (signed) G[x].size(); ++ i) { Edge &e = edges[G[x][i]]; if(!vis[e.to] && e.cap > e.flow) { vis[e.to] = true; dis[e.to] = dis[x] + 1; que[++ tail] = e.to; } } ++ head; } return vis[t]; } int dfs(int x, int a) { if(x == t || a == 0) return a; int flw = 0, f; for(int &i = cur[x]; i < (signed) G[x].size(); ++ i) { Edge &e = edges[G[x][i]]; if(dis[e.to] == dis[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) { e.flow += f; edges[G[x][i] ^ 1].flow -= f; a -= f; flw += f; if(a == 0) break; } } return flw; } int maxflow(int s, int t) { this->s = s; this->t = t; int flw = 0; while(bfs()) { memset(cur, 0, sizeof cur); flw += dfs(s, oo); } return flw; } void rebuild() { for(int i = 0; i < (signed) edges.size(); ++ i) edges[i].flow = 0; } void dfs(int u) { mark[u] = true; for(int i = 0; i < (signed) G[u].size(); ++ i) { Edge e = edges[G[u][i]]; if(!mark[e.to] && e.cap > e.flow) dfs(e.to); } } }net; void Divide(int l, int r) { if(l >= r) return; net.rebuild(); int nowflow = net.maxflow(a[l], a[r]); memset(mark, false, (n + 1) * sizeof(bool)); net.dfs(a[l]); for(int i = 1; i <= n; ++ i) if(mark[i]) for(int j = 1; j <= n; ++ j) if(!mark[j]) ans[i][j] = min(ans[i][j], nowflow), ans[j][i] = ans[i][j]; int L = l, R = r; for(int i = l; i <= r; ++ i) if(mark[a[i]]) tmp[L ++] = a[i]; else tmp[R --] = a[i]; for(int i = l; i <= r; ++ i) a[i] = tmp[i]; Divide(l, L - 1); Divide(R + 1, r); } int main() { int u, v, c; scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++ i) { scanf("%d%d%d", &u, &v, &c); net.add(u, v, c); } for(int i = 1; i <= n; ++ i) a[i] = i; for(int i = 1; i <= n; ++ i) for(int j = 1; j <= n; ++ j) ans[i][j] = oo; net.n = n; Divide(1, n); for(int i = 1; i <= n; ++ i) { for(int j = i + 1; j <= n; ++ j) { lts.insert(ans[i][j]); } } printf("%d\n", lts.size()); return 0; }