图论模板
最短路(dijkstra)
无法处理负边权,时间复杂度O(mlogn)
#include<bits/stdc++.h>
#define fo(i,a,b) for (ll (i)=(a);(i)<=(b);(i)++)
#define fd(i,b,a) for (ll (i)=(b);(i)>=(a);(i)--)
#define lc (o<<1)
#define rc ((o<<1)|1)
#define mk(x,y) make_pair((x),(y))
#define eb emplace_back
//#define A puts("Yes")
//#define B puts("No")
#define fi first
#define se second
#define pi pair<ll,ll>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef long long i64;
typedef unsigned int uint;
const int N=1e6+5;
const ll mo=1e9+7;
const int inf=1e9;
//set<int,greater<int>> s;
priority_queue<pair<i64, int>, vector<pair<i64, int>>, greater<pair<i64, int>>> q;
int tot=1,nex[N*2],head[N],to[N],n,m,s,x,y,z;
ll w[N*2],d[N];
bool vis[N];
void add(int x,int y,int z){
to[++tot]=y; nex[tot]=head[x]; head[x]=tot; w[tot]=z;
}
void dij(int s){
fo(i,1,n) d[i]=inf;
d[s]=0;
q.push(mk(0,s));
while (!q.empty()) {
x=q.top().se;
q.pop();
if (vis[x]) continue;
vis[x]=1;
for (int i=head[x];i;i=nex[i]) {
int v=to[i];
if (d[v]>d[x]+w[i]) {
d[v]=d[x]+w[i];
q.push(mk(d[v],v));
}
}
}
}
int main() {
// freopen("data.in", "r", stdin);
// freopen("data.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m>>s;
fo(i,1,m) {
cin>>x>>y>>z;
add(x,y,z);
}
dij(s);
fo(i,1,n) cout<<d[i]<<" ";
return 0;
}
abc375_g
判断某条边是不是1到n最短路上的必经边
dis1[x],f[x]分别表示1-x的最短路长度以及在最短路长度限制下到达x的不同路径数量,
dis2,g表示从n出发的
若(x,y)为必经边,则\(dis1[x]+w(x,y)+dis2[y]=dis1[n]\)且\(f[x]*g[y]=f[n]\)
#include<bits/stdc++.h>
#define fo(i,a,b) for (ll (i)=(a);(i)<=(b);(i)++)
#define eb emplace_back
#define pi pair<ll,ll>
#define mk(x,y) make_pair((x),(y))
#define fi first
#define se second
using namespace std;
typedef long long ll;
const ll inf = 1ll << 60;
const int N = 2e5 + 10;
const int mo = 998244353;
const ll mo1 = 1e9 + 7;
const ll mo2 = 1e9 + 9;
ll n, m, x, y, z, w[N * 2], dis1[N], dis2[N];
int tot = 1, to[N * 2], nex[N * 2], head[N], d[N];
pi f[N], g[N];
bool vis[N];
priority_queue<pi, vector<pi>, greater<pi>> q;
void add(int x, int y, ll z) {
to[++tot] = y; nex[tot] = head[x]; head[x] = tot; w[tot] = z;
}
void dij(int s, pi f[], ll dis[]) {
fo(i, 1, n) dis[i] = inf, vis[i] = 0;
dis[s] = 0;
q.push(mk(0, s));
while (!q.empty()) {
x = q.top().se; q.pop();
if (vis[x]) continue;
vis[x] = 1;
for (int i = head[x];i;i = nex[i]) {
int v = to[i];
if (dis[v] > dis[x] + w[i]) {
dis[v] = dis[x] + w[i];
q.push(mk(dis[v], v));
}
}
}
fo(i, 1, n) d[i] = 0;
fo(i, 2, tot) {
x = to[i]; y = to[i ^ 1]; z = w[i];
if (dis[x] + w[i] == dis[y]) {
d[y]++;
}
}
queue<int> q;
while (!q.empty()) q.pop();
fo(i, 1, n) if (!d[i]) q.push(i), f[i] = mk(1, 1);
while (!q.empty()) {
x = q.front(); q.pop();
for (int i = head[x];i;i = nex[i]) {
int v = to[i];
if (dis[v] == dis[x] + w[i]) {
d[v]--;
f[v] = mk((f[v].fi + f[x].fi) % mo1, (f[v].se + f[x].se) % mo2);
if (!d[v]) q.push(v);
}
}
}
}
bool ans[N * 2];
void solve()
{
cin >> n >> m;
fo(i, 1, m) {
cin >> x >> y >> z;
add(x, y, z);
add(y, x, z);
}
dij(1, f, dis1);
dij(n, g, dis2);
fo(i, 2, tot) {
x = to[i]; y = to[i ^ 1]; z = w[i];
if (f[x].fi * g[y].fi % mo1 == f[n].fi && f[x].se * g[y].se % mo2 == f[n].se && dis1[x]+w[i]+dis2[y]==dis1[n]) {
// cout << x << " " << y << "\n";
ans[i / 2] = 1;
}
}
fo(i, 1, m) cout << (ans[i] ? "Yes" : "No") << "\n";
}
int main()
{
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
Floyed
abc375f
两种操作
1.路径i不再可以通行
2.询问dis(x,y)
显然将询问离线,然后倒着加边,每次加入一条边后,考虑更新。
此时f[x][y]表示的是只通过当前加入的边的情况下,x到y的最短路,那么我们枚举通过新加这条边的路径的两个端点即可。
#include<bits/stdc++.h>
#define ll long long
#define fo(i,a,b) for(ll (i)=(a);(i)<=(b);(i)++)
#define fd(i,a,b) for(ll (i)=(a);(i)>=(b);(i)--)
#define eb emplace_back
#define mk make_pair
using namespace std;
const int N = 505;
const int M = 2e5 + 5;
const ll inf = 1ll << 60;
ll f[N][N], n, m, q, ans[M], tot;
bool bz[M];
struct node {
ll x, y, z;
};
node e[M];
struct key {
ll op, x, y;
};
key c[M];
void cmin(ll& x, ll y) {
x = min(x, y);
}
void solve() {
cin >> n >> m >> q;
fo(i, 1, m) cin >> e[i].x >> e[i].y >> e[i].z;
fo(i, 1, q) {
cin >> c[i].op >> c[i].x;
if (c[i].op == 2) {
cin >> c[i].y;
}
else bz[c[i].x] = 1;
}
fo(i, 1, n) fo(j, 1, n) f[i][j] = inf;
fo(i, 1, n) f[i][i] = 0;
fo(i, 1, m) if (!bz[i]) {
cmin(f[e[i].x][e[i].y], e[i].z);
cmin(f[e[i].y][e[i].x], e[i].z);
}
fo(k, 1, n) fo(i, 1, n) fo(j, 1, n) cmin(f[i][j], f[i][k] + f[k][j]);
ll id, x, y, z;
fd(i, q, 1) {
id = c[i].x;
x = e[id].x; y = e[id].y; z = e[id].z;
if (c[i].op == 1) {
fo(a, 1, n) fo(b, 1, n) {
cmin(f[a][b], f[a][x] + z + f[y][b]);
cmin(f[a][b], f[a][y] + z + f[x][b]);
}
}
else {
x = c[i].x; y = c[i].y;
if (f[x][y] == inf) ans[++tot] = -1;
else ans[++tot] = f[x][y];
}
}
fd(i, tot, 1) cout << ans[i] << "\n";
}
int main() {
// freopen("data.in","r",stdin);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin>>T;
while (T--) {
solve();
}
return 0;
}
传递闭包
跟floyed一样,可以采用bitset优化
#include<bits/stdc++.h>
#define fo(i,a,b) for (ll (i)=(a);(i)<=(b);(i)++)
#define fd(i,b,a) for (ll (i)=(b);(i)>=(a);(i)--)
#define eb emplace_back
using namespace std;
const int N=1005;
typedef long long ll;
bitset<N> b[N];
int n,x;
int main(){
// freopen("data.in","r",stdin);
scanf("%d",&n);
fo(i,1,n) {
b[i].reset();
fo(j,1,n) {
scanf("%d",&x);
if (x) {
b[i][j]=1;
}
}
}
fo(k,1,n) {
fo(i,1,n) if (b[i][k]) b[i]|=b[k];
}
fo(i,1,n) {
fo(j,1,n) {
printf("%d ",b[i][j]==1?1:0);
}
printf("\n");
}
return 0;
}
差分约束
[ABC216G] 01Sequence
你需要构造出一个长度为 \(n\) 的 \(01\) 序列,满足 \(m\) 个限制 \((l_i,r_i,x_i)\):在 \([l_i,r_i]\) 这段区间内,序列上 \(1\) 的个数不小于 \(x_i\)。你需要保证你的方案中包含 \(1\) 的个数最小。
数据保证有解。
\(1 \le n,m \le 2 \times 10^5\)
设\(z_i\)表示1到i中0的数量,那么限制有
- \(z_i \le z_{i-1}+1\)
- \(z_{i-1} \le z_{i}\)
- \(z_{r_i}-z_{l_i-1}\le r_i-l_i+1-x_i\)
- \(z_0=0\)
将第三个变形,得到\(z_{r_i}\le r_i-l_i+1-x_i+z_{l_i-1}\)
那么我们得到的就是最短路的形式,因为我们求的是\(z_n\)的最大值,最大能够取得就是不等式右边的最小值,所以等价于最短路,因为所有边权都是正的,直接用dij即可。
#include<bits/stdc++.h>
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define fd(i,b,a) for (int (i)=(b);(i)>=(a);(i)--)
#define lc (o<<1)
#define rc ((o<<1)|1)
#define mk(x,y) make_pair((x),(y))
#define pi pair<ll,ll>
#define eb emplace_back
//#define A puts("YES")
//#define B puts("NO")
#define fi first
#define se second
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef long long i64;
//const ll mo1=1e9+7;
//const ll mo2=1e9+9;
const ll mo = 19260817;
const ll P = 131;
const ll Q = 13331;
const ll inf = 1ll << 60;
const int N = 2e5 + 5;
const int M = 1e6 + 5;
int tot = 1, to[M * 2], nex[M * 2], head[N], w[M * 2], d[N];
int n, m;
bool vis[N];
void add(int x, int y, int z) {
to[++tot] = y; nex[tot] = head[x]; head[x] = tot; w[tot] = z;
}
void dij(int s) {
priority_queue<pi, vector<pi>, greater<pi>> q;
fo(i, 1, n) d[i] = n;
q.push(mk(0, s));
while (!q.empty()) {
int x = q.top().se; q.pop();
if (vis[x]) continue;
vis[x] = 1;
for (int i = head[x];i;i = nex[i]) {
int v = to[i];
if (d[v] > d[x] + w[i]) {
d[v] = d[x] + w[i];
q.push(mk(d[v], v));
}
}
}
}
int main() {
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
fo(i, 1, n) {
add(i - 1, i, 1);
add(i, i - 1, 0);
}
int l, r, x;
fo(i, 1, m) {
cin >> l >> r >> x;
add(l - 1, r, r - l + 1 - x);
}
dij(0);
// fo(i,1,n) cout<<d[i]<<" ";
//
// return 0;
fo(i, 1, n) cout << -d[i] + 1 + d[i - 1] << " ";
}
two-sat
[ABC210F] Coprime Solitaire
题面翻译
你有 \(n\) 张卡片,第 \(i\) 张卡片正面写着一个数字 \(a_i\),反面写着一个数字 \(b_i\),现在你可以摆放卡片(规定每张卡片朝上的面),询问是否存在一种摆放方式,使得任意两张不同的卡片朝上面所写的数字都是互质的。
- 首先有一个非常明显的\(O(N^2)\)的做法,就是直接两两之间根据矛盾关系连边,但是有更好的方法
- 对于每一个质数,我们都开一个vector记录哪些数有这个质因子,那么这个集合中最多只能选一个数,因此我们可以用前后缀连边优化。
- 以前缀为例说,\(f[i]\)连向\(f[i-1]\)和\(\neg x\),然后\(\neg x\)连向\(f[i-1]\),后缀同理
#include<bits/stdc++.h>
#define fo(i,a,b) for(ll (i)=(a);(i)<=(b);(i)++)
#define fd(i,a,b) for(ll (i)=(a);(i)>=(b);(i)--)
#define mk(x,y) make_pair((x),(y))
#define pi pair<ll,ll>
#define eb emplace_back
#define fi first
#define se second
using namespace std;
typedef long long ll;
const ll mo = 1e9 + 7;
const ll inf = 1ll << 60;
const int N = 2e6 + 5;
int tot, p[N], v[N], a[N * 2],n;
bool vis[N];
vector<int> t[N], e[N];
int dfn[N], low[N], s[N], top, cnt, z, scc[N], num;
bool ins[N];
void tarjan(int x) {
low[x] = dfn[x] = ++cnt;
s[++top] = x;
ins[x] = 1;
for (int v : e[x]) {
if (!dfn[v]) {
tarjan(v);
low[x] = min(low[x], low[v]);
}
else if (ins[v]) low[x] = min(low[x], dfn[v]);
}
if (low[x] == dfn[x]) {
++num;
do {
z = s[top--]; scc[z] = num; ins[z] = 0;
} while (z != x);
}
}
void work(int x, int id) {
int d;
while (x != 1) {
d = v[x];
while (x % d == 0) {
x /= d;
}
t[d].eb(id);
}
}
int rev(int x) { return x > n ? x - n : x + n; }
int main() {
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
fo(i, 2, N - 1) {
if (!vis[i]) {
p[++tot] = i;
v[i] = i;
}
fo(j, 1, tot) {
if (i * p[j] >= N) break;
vis[i * p[j]] = 1;
v[i * p[j]] = p[j];
if (i % p[j] == 0) break;
}
}
cin >> n;
fo(i, 1, n) {
cin >> a[i] >> a[i + n];
work(a[i], i);
work(a[i + n], i + n);
}
int now = 2 * n;
fo(i, 1, tot) {
int z = p[i];
if (!t[z].size()) continue;
// for (auto x : t[z]) cout << x << " ";
// cout << "\n";
now++;
e[now].eb(rev(t[z][0]));
for (int j = 1;j < (int)t[z].size();j++) {
e[t[z][j]].eb(now);
now++;
e[now].eb(now - 1);
e[now].eb(rev(t[z][j]));
}
now++;
e[now].eb(rev(t[z][t[z].size() - 1]));
for (int j = (int)t[z].size() - 2;j >= 0;j--) {
e[t[z][j]].eb(now);
now++;
e[now].eb(now - 1);
e[now].eb(rev(t[z][j]));
}
}
fo(i, 1, now) if (!dfn[i]) tarjan(i);
bool flag = 1;
fo(i, 1, n) {
if (a[i] != 1 && a[i + n] != 1) {
if (scc[i] == scc[i + n]) {
flag = 0;
}
}
}
if (flag) cout << "Yes";
else cout << "No";
return 0;
}
三元环计数
#include <bits/stdc++.h>
using namespace std;
int n, m, total;
int deg[200001], u[200001], v[200001];
bool vis[200001];
struct Edge {
int to, nxt;
} edge[200001];
int cntEdge, head[100001];
void addEdge(int u, int v) {
edge[++cntEdge] = {v, head[u]}, head[u] = cntEdge;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
scanf("%d%d", u + i, v + i), deg[u[i]]++, deg[v[i]]++;
for (int i = 1; i <= m; i++) {
if ((deg[u[i]] == deg[v[i]] && u[i] > v[i]) || deg[u[i]] < deg[v[i]])
swap(u[i], v[i]);
addEdge(u[i], v[i]);
}
for (int u = 1; u <= n; u++) {
for (int i = head[u]; i; i = edge[i].nxt) vis[edge[i].to] = true;
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
for (int j = head[v]; j; j = edge[j].nxt) {
int w = edge[j].to;
if (vis[w]) total++;
}
}
for (int i = head[u]; i; i = edge[i].nxt) vis[edge[i].to] = false;
}
printf("%d\n", total);
return 0;
}
四元环计数
#include <bits/stdc++.h>
using namespace std;
int n, m, deg[100001], cnt[100001];
vector<int> E[100001], E1[100001];
long long total;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
E[u].push_back(v);
E[v].push_back(u);
deg[u]++, deg[v]++;
}
for (int u = 1; u <= n; u++)
for (int v : E[u])
if (deg[u] > deg[v] || (deg[u] == deg[v] && u > v)) E1[u].push_back(v);
for (int a = 1; a <= n; a++) {
for (int b : E1[a])
for (int c : E[b]) {
if (deg[a] < deg[c] || (deg[a] == deg[c] && a <= c)) continue;
total += cnt[c]++;
}
for (int b : E1[a])
for (int c : E[b]) cnt[c] = 0;
}
printf("%lld\n", total);
return 0;
}
最大流
#include<bits/stdc++.h>
#define fo(i,a,b) for (ll (i)=(a);(i)<=(b);(i)++)
#define fd(i,b,a) for (ll (i)=(b);(i)>=(a);(i)--)
#define lc (o<<1)
#define rc ((o<<1)|1)
#define mk(x,y) make_pair((x),(y))
#define eb emplace_back
#define A puts("Yes")
#define B puts("No")
#define fi first
#define se second
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef long long i64;
// const ll inf=1ll<<60;
const ll mo1=1e9+9;
const ll mo2=1e9+7;
const ll P=131;
const ll Q=13331;
constexpr int inf = 1E9;
template<class T>
struct MaxFlow {
struct _Edge {
int to;
T cap;
_Edge(int to, T cap) : to(to), cap(cap) {}
};
int n;
std::vector<_Edge> e;
std::vector<std::vector<int>> g;
std::vector<int> cur, h;
MaxFlow() {}
MaxFlow(int n) {
init(n);
}
void init(int n) {
this->n = n;
e.clear();
g.assign(n, {});
cur.resize(n);
h.resize(n);
}
bool bfs(int s, int t) {
h.assign(n, -1);
std::queue<int> que;
h[s] = 0;
que.push(s);
while (!que.empty()) {
const int u = que.front();
que.pop();
for (int i : g[u]) {
// auto [v, c] = e[i];
auto v = e[i].to;
auto c = e[i].cap;
if (c > 0 && h[v] == -1) {
h[v] = h[u] + 1;
if (v == t) {
return true;
}
que.push(v);
}
}
}
return false;
}
T dfs(int u, int t, T f) {
if (u == t) {
return f;
}
auto r = f;
for (int &i = cur[u]; i < int(g[u].size()); ++i) {
const int j = g[u][i];
// auto [v, c] = e[j];
auto v = e[j].to;
auto c = e[j].cap;
if (c > 0 && h[v] == h[u] + 1) {
auto a = dfs(v, t, std::min(r, c));
e[j].cap -= a;
e[j ^ 1].cap += a;
r -= a;
if (r == 0) {
return f;
}
}
}
return f - r;
}
void addEdge(int u, int v, T c) {
g[u].push_back(e.size());
e.emplace_back(v, c);
g[v].push_back(e.size());
e.emplace_back(u, 0);
}
T flow(int s, int t) {
T ans = 0;
while (bfs(s, t)) {
cur.assign(n, 0);
ans += dfs(s, t, std::numeric_limits<T>::max());
}
return ans;
}
std::vector<bool> minCut() {
std::vector<bool> c(n);
for (int i = 0; i < n; i++) {
c[i] = (h[i] != -1);
}
return c;
}
struct Edge {
int from;
int to;
T cap;
T flow;
};
std::vector<Edge> edges() {
std::vector<Edge> a;
for (int i = 0; i < e.size(); i += 2) {
Edge x;
x.from = e[i + 1].to;
x.to = e[i].to;
x.cap = e[i].cap + e[i + 1].cap;
x.flow = e[i + 1].cap;
a.push_back(x);
}
return a;
}
};
int n,m,s,t,x,y,z;
int main(){
cin >> n >> m >> s >> t;
MaxFlow<ll> mf(n + 2);
fo(i, 1, m) {
cin >> x >> y >> z;
mf.addEdge(x, y, z);
}
ll ans = mf.flow(s, t);
cout << ans;
return 0;
}
最小费用最大流
#include<bits/stdc++.h>
#define fo(i,a,b) for (ll (i)=(a);(i)<=(b);(i)++)
#define fd(i,b,a) for (ll (i)=(b);(i)>=(a);(i)--)
#define lc (o<<1)
#define rc ((o<<1)|1)
#define mk(x,y) make_pair((x),(y))
#define eb emplace_back
#define A puts("Yes")
#define B puts("No")
#define fi first
#define se second
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef long long i64;
const ll inf=1ll<<60;
const ll mo1=1e9+9;
const ll mo2=1e9+7;
const ll P=131;
const ll Q=13331;
const int N=2e5+5;
struct MCFGraph {
struct Edge {
int v, c, f;
Edge(int v, int c, int f) : v(v), c(c), f(f) {}
};
const int n;
std::vector<Edge> e;
std::vector<std::vector<int>> g;
std::vector<i64> h, dis;
std::vector<int> pre;
bool dijkstra(int s, int t) {
dis.assign(n, std::numeric_limits<i64>::max());
pre.assign(n, -1);
std::priority_queue<std::pair<i64, int>, std::vector<std::pair<i64, int>>, std::greater<std::pair<i64, int>>> que;
dis[s] = 0;
que.emplace(0, s);
while (!que.empty()) {
i64 d = que.top().first;
int u = que.top().second;
que.pop();
if (dis[u] < d) continue;
for (int i : g[u]) {
int v = e[i].v;
int c = e[i].c;
int f = e[i].f;
if (c > 0 && dis[v] > d + h[u] - h[v] + f) {
dis[v] = d + h[u] - h[v] + f;
pre[v] = i;
que.emplace(dis[v], v);
}
}
}
return dis[t] != std::numeric_limits<i64>::max();
}
MCFGraph(int n) : n(n), g(n) {}
void addEdge(int u, int v, int c, int f) { // 最大流
g[u].push_back(e.size());
e.emplace_back(v, c, f);
g[v].push_back(e.size());
e.emplace_back(u, 0, -f);
}
std::pair<int, i64> flow(int s, int t) {
int flow = 0;
i64 cost = 0;
h.assign(n, 0);
while (dijkstra(s, t)) {
for (int i = 0; i < n; ++i) h[i] += dis[i];
int aug = std::numeric_limits<int>::max();
for (int i = t; i != s; i = e[pre[i] ^ 1].v) aug = std::min(aug, e[pre[i]].c);
for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
e[pre[i]].c -= aug;
e[pre[i] ^ 1].c += aug;
}
flow += aug;
cost += i64(aug) * h[t];
}
return std::make_pair(flow, cost);
}
};
int n,m,s,t,x,y,w,c;
int main(){
// freopen("data.in","r",stdin);
scanf("%d %d %d %d",&n,&m,&s,&t);
MCFGraph mf(n+1);
fo(i,1,m) {
scanf("%d %d %d %d",&x,&y,&w,&c);
mf.addEdge(x,y,w,c);
}
pair<int,ll> h=mf.flow(s,t);
printf("%d %lld", h.fi, h.se);
return 0;
}
最大费用可行流
建边时费用取反,剩下的mcmf一样
#include<bits/stdc++.h>
#define fo(i,a,b) for (ll (i)=(a);(i)<=(b);(i)++)
#define fd(i,b,a) for (ll (i)=(b);(i)>=(a);(i)--)
#define lc (o<<1)
#define rc ((o<<1)|1)
#define mk(x,y) make_pair((x),(y))
#define eb emplace_back
#define A puts("Yes")
#define B puts("No")
#define fi first
#define se second
#define pi pair<ll,ll>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef long long i64;
const ll inf=1ll<<60;
const ll mo1=1e9+9;
const ll mo2=1e9+7;
const ll P=131;
const ll Q=13331;
const int N=2e5+5;
ll ans=1e9;
struct MCFGraph {
struct Edge {
int v, c, f;
Edge(int v, int c, int f) : v(v), c(c), f(f) {}
};
const int n;
std::vector<Edge> e;
std::vector<std::vector<int>> g;
std::vector<i64> h, dis;
std::vector<int> pre;
bool dijkstra(int s, int t) {
dis.assign(n, std::numeric_limits<i64>::max());
pre.assign(n, -1);
std::priority_queue<std::pair<i64, int>, std::vector<std::pair<i64, int>>, std::greater<std::pair<i64, int>>> que;
dis[s] = 0;
que.emplace(0, s);
while (!que.empty()) {
i64 d = que.top().first;
int u = que.top().second;
que.pop();
if (dis[u] < d) continue;
for (int i : g[u]) {
int v = e[i].v;
int c = e[i].c;
int f = e[i].f;
if (c > 0 && dis[v] > d + h[u] - h[v] + f) {
dis[v] = d + h[u] - h[v] + f;
pre[v] = i;
que.emplace(dis[v], v);
}
}
}
return dis[t] != std::numeric_limits<i64>::max();
}
MCFGraph(int n) : n(n), g(n) {}
// void addEdge(int u, int v, int c, int f) {
// if (f < 0) {
// g[u].push_back(e.size());
// e.emplace_back(v, 0, f);
// g[v].push_back(e.size());
// e.emplace_back(u, c, -f);
// } else {
// g[u].push_back(e.size());
// e.emplace_back(v, c, f);
// g[v].push_back(e.size());
// e.emplace_back(u, 0, -f);
// }
//
// }
void addEdge(int u, int v, int c, int f) { // �����
g[u].push_back(e.size());
e.emplace_back(v, c, f);
g[v].push_back(e.size());
e.emplace_back(u, 0, -f);
}
std::pair<ll, i64> flow(int s, int t) {
int flow = 0;
i64 cost = 0;
h.assign(n, 0);
while (dijkstra(s, t)) {
for (int i = 0; i < n; ++i) h[i] += dis[i];
int aug = std::numeric_limits<int>::max();
for (int i = t; i != s; i = e[pre[i] ^ 1].v) aug = std::min(aug, e[pre[i]].c);
for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
e[pre[i]].c -= aug;
e[pre[i] ^ 1].c += aug;
}
flow += aug;
cost += i64(aug) * h[t];
ans=min(ans, cost);//与mcmf的区别
}
return std::make_pair(flow, cost);
}
};
int n,m,k,a[105][105],s,t,x,y;
int w[4][2]={
-1,0,
1,0,
0,1,
0,-1
};
int In(int x,int y){
return (x-1)*m+y;
}
int Out(int x,int y){
return (x-1)*m+y+n*m;
}
bool check(int x,int y){
return (1<=x && x<=n && 1<=y && y<=m && a[x][y]!=-1);
}
int main(){
// freopen("data.in","r",stdin);
scanf("%d %d %d",&n,&m,&k);
fo(i,1,n) {
fo(j,1,m) {
scanf("%d",&a[i][j]);
}
}
s=2*n*m+1; t=2*n*m+2;
MCFGraph mf(2*n*m+3);
fo(i,1,k) {
scanf("%d %d",&x,&y);
mf.addEdge(s,In(x,y),1,0);
}
fo(i,1,k) {
scanf("%d %d",&x,&y);
mf.addEdge(Out(x,y),t,1,-100);
}
int xx,yy;
fo(x,1,n) fo(y,1,m) {
if (a[x][y]==-1) continue;
mf.addEdge(In(x,y),Out(x,y),1,1-a[x][y]);
fo(k,0,3) {
xx=x+w[k][0];
yy=y+w[k][1];
if (check(xx,yy)) {
mf.addEdge(Out(x,y),In(xx,yy),1,0);
}
}
}
pi h=mf.flow(s,t);
printf("%lld\n",max(-ans,0ll));
return 0;
}