#include <stdio.h> #include <vector> #include <algorithm> using namespace std; #define INF 1111111111 // things that handle max flow struct edge { int j, flow, cap; edge *pair; edge(int j, int cap): j(j), flow(0), cap(cap), pair(NULL) {} }; struct flow { int n; vector<edge*> *adj; edge **parent; int *flowto; flow(int n): n(n) { adj = new vector<edge*>[n]; for (int i = 0; i < n; i++) { adj[i].clear(); } parent = new edge*[n]; flowto = new int[n]; } void add_edge(int i, int j, int front, int back = 0) { // back is the capacity of the backward direction. // if back = 0, then edge is directed. edge *ef = new edge(j, front); edge *eb = new edge(i, back); ef->pair = eb; eb->pair = ef; adj[i].push_back(ef); adj[j].push_back(eb); } int aug(int s, int t) { // BFS-augment from s to t vector<int> queue; for (int i = 0; i < n; i++) flowto[i] = 0; queue.push_back(s); flowto[s] = INF; for (int f = 0; f < queue.size(); f++) { int i = queue[f]; for (int nb = 0; nb < adj[i].size(); nb++) { edge *e = adj[i][nb]; int j = e->j; if (!flowto[j]) { int delta = e->cap - e->flow; if (delta > 0) { flowto[j] = min(flowto[i], delta); parent[j] = e; if (j == t) return flowto[j]; queue.push_back(j); } } } } return 0; } int max_flow(int s, int t) { // edmonds karp for (int flow = 0;;) { int res = aug(s, t); if (!res) return flow; flow += res; for (int j = t; j != s; j = parent[j]->pair->j) { parent[j]->flow += res; parent[j]->pair->flow -= res; } } } }; typedef pair<int,int> wall; wall walls[151111]; struct cell { int idx, i, j; int parent; // union find parent int label; // index in input bool d, r; // whether "down" or "right" is walkable cell() {} cell(int idx, int i, int j, bool d, bool r): idx(idx), i(i), j(j), parent(-1), label(-1), d(d), r(r) {} }; #define IDX(i,j) ((i)*c+(j)) cell cells[1111111]; int r, c; // union find int find(int n) { return cells[n].parent < 0 ? n : cells[n].parent = find(cells[n].parent); } void onion(int a, int b) { a = find(a); b = find(b); if (a == b) return; if (cells[a].parent == cells[b].parent) cells[b].parent--; if (cells[a].parent > cells[b].parent) { cells[a].parent = b; } else { cells[b].parent = a; } } // temporary matrix int mat[511][511]; int main() { int w, cost, n, comps; scanf("%d%d%d%d%d", &r, &c, &w, &cost, &n); // initialize grid for (int i = 0; i < r; i++) { for (int j = 0; j < c; j++) { cells[IDX(i,j)] = cell(IDX(i,j), i, j, i != r-1, j != c-1); } } // collect walls for (int i = 0; i < w; i++) { int x1, y1, x2, y2; scanf("%d%d%d%d", &x1, &y1, &x2, &y2); x1--, y1--, x2--, y2--; if (x1 > x2) swap(x1, x2); if (y1 > y2) swap(y1, y2); int i1 = IDX(x1,y1); int i2 = IDX(x2,y2); walls[i] = wall(i1, i2); if (x1 == x2) { cells[i1].r = false; } else { cells[i1].d = false; } } // unite adjacent cells into rooms for (int i = 0; i < r; i++) { for (int j = 0; j < c; j++) { int I = IDX(i,j); if (cells[I].d) { onion(IDX(i,j), IDX(i+1,j)); } if (cells[I].r) { onion(IDX(i,j), IDX(i,j+1)); } } } // initialize flow flow *f = new flow(n+2); int S = n, T = n+1, A = 0; // compute edges with source / sink for (int i = 0; i < n; i++) { int x, y, c1, c2; scanf("%d%d%d%d", &x, &y, &c1, &c2); x--, y--; cells[find(IDX(x,y))].label = i; int m = min(c1, c2); c1 -= m; c2 -= m; A += m; if (c1) f->add_edge(S, i, c1); if (c2) f->add_edge(i, T, c2); } // compute number of walls between rooms for (int k = 0; k < w; k++) { int a = cells[find(walls[k].first)].label; int b = cells[find(walls[k].second)].label; if (~a && ~b && a != b) mat[a][b]++, mat[b][a]++; } // add edges between rooms for (int i = 0; i < n; i++) { for (int j = 0; j < i; j++) { if (mat[i][j]) { int c = mat[i][j] * cost; f->add_edge(i, j, c, c); } } } // flow! printf("%d\n", A + f->max_flow(S, T)); }