//連結成分ごとにやるんだけど、実装方針で悩むなあ…
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cassert>
#define int long long
#define rep(i, n) for(i = 0; i < n; i++)
using namespace std;

int R, C;
int N;
int r[100000], c[100000], a[100000];
vector<int> et[200000];
vector<int> ec[200000];

signed main() {
	int i, j;
	
	cin >> R >> C;
	cin >> N;
	rep(i, N) {
		cin >> r[i] >> c[i] >> a[i];
		r[i]--; c[i]--;
		et[r[i]].push_back(c[i] + R);
		ec[r[i]].push_back(a[i]);
		et[c[i] + R].push_back(r[i]);
		ec[c[i] + R].push_back(a[i]);
	}
	
	static vector<int> etC[100000];
	static vector<int> ecC[100000];
	rep(i, R) {
		rep(j, (int)et[i].size() - 1) {
			int c1 = et[i][j] - R;
			int c2 = et[i][j + 1] - R;
			etC[c1].push_back(c2);
			ecC[c1].push_back(ec[i][j + 1] - ec[i][j]);
			etC[c2].push_back(c1);
			ecC[c2].push_back(ec[i][j] - ec[i][j + 1]);
		}
	}
	
	static bool visC[100000] = {false};
	static int valC[100000];
	rep(i, C) {
		if (visC[i]) continue;
		
		queue<int> que;
		visC[i] = true;
		valC[i] = 0;
		que.push(i);
		
		while (!que.empty()) {
			int v = que.front(); que.pop();
			rep(j, etC[v].size()) {
				int nv = etC[v][j];
				if (!visC[nv]) {
					visC[nv] = true;
					valC[nv] = valC[v] + ecC[v][j];
					que.push(nv);
				}
				else if (valC[nv] != valC[v] + ecC[v][j]) {
					cout << "No" << endl;
					return 0;
				}
			}
		}
	}
	
	static vector<int> etR[100000];
	static vector<int> ecR[100000];
	for (i = R; i < R + C; i++) {
		rep(j, (int)et[i].size() - 1) {
			int r1 = et[i][j];
			int r2 = et[i][j + 1];
			etR[r1].push_back(r2);
			ecR[r1].push_back(ec[i][j + 1] - ec[i][j]);
			etR[r2].push_back(r1);
			ecR[r2].push_back(ec[i][j] - ec[i][j + 1]);
		}
	}
	
	static bool visR[100000] = {false};
	static int valR[100000];
	rep(i, C) {
		if (visR[i]) continue;
		
		queue<int> que;
		visR[i] = true;
		valR[i] = 0;
		que.push(i);
		
		while (!que.empty()) {
			int v = que.front(); que.pop();
			rep(j, etR[v].size()) {
				int nv = etR[v][j];
				if (!visR[nv]) {
					visR[nv] = true;
					valR[nv] = valR[v] + ecR[v][j];
					que.push(nv);
				}
				else if (valR[nv] != valR[v] + ecR[v][j]) {
					cout << "No" << endl;
					return 0;
				}
			}
		}
	}
	
	static bool vis[200000] = {false};
	rep(i, R + C) {
		queue<int> que;
		vector<int> vs;
		
		if (vis[i]) continue;
		que.push(i);
		vis[i] = true;
		while (!que.empty()) {
			int v = que.front(); que.pop();
			vs.push_back(v);
			rep(j, et[v].size()) {
				int nv = et[v][j];
				if (vis[nv]) continue;
				vis[nv] = true;
				que.push(nv);
			}
		}
		if (vs.size() <= 2) { continue; }
		
		int INF = 1e+15;
		int rmin = INF, cmin = INF;
		rep(j, vs.size()) {
			if (vs[j] < R) {
				rmin = min(rmin, valR[vs[j]]);
			}
			else {
				cmin = min(cmin, valC[vs[j] - R]);
			}
		}
		
		int rc;	//r + c. valR = 0, valC = 0の頂点に書き込む値の合計.
		bool isFind = false;	//デバッグ用
		rep(j, vs.size()) {
			if (vs[j] >= R) continue;
			int row = vs[j];
			int col = et[vs[j]][0] - R;
			rc = ec[vs[j]][0] - (valR[row] + valC[col]);
			isFind = true;
			break;
		}
		if (isFind == false) { assert(0); }
		
		if (rmin + cmin + rc < 0) {
			cout << "No" << endl;
			return 0;
		}
	}
	
	cout << "Yes" << endl;
	return 0;
}

Measured memory_bytes: 43288