// https://atcoder.jp/contests/joi2014ho/submissions/2337234 #include using namespace std; using int64 = long long; const int mod = 1e9 + 7; // const int mod = 998244353; const int64 infll = (1LL << 62) - 1; const int inf = (1 << 30) - 1; struct IoSetup { IoSetup() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(10); cerr << fixed << setprecision(10); } } iosetup; template< typename T1, typename T2 > ostream &operator<<(ostream &os, const pair< T1, T2 > &p) { os << p.first << " " << p.second; return os; } template< typename T1, typename T2 > istream &operator>>(istream &is, pair< T1, T2 > &p) { is >> p.first >> p.second; return is; } template< typename T > ostream &operator<<(ostream &os, const vector< T > &v) { for(int i = 0; i < (int) v.size(); i++) { os << v[i] << (i + 1 != v.size() ? " " : ""); } return os; } template< typename T > istream &operator>>(istream &is, vector< T > &v) { for(T &in: v) is >> in; return is; } template< typename T1, typename T2 > inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); } template< typename T1, typename T2 > inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); } template< typename T = int64 > vector< T > make_v(size_t a) { return vector< T >(a); } template< typename T, typename... Ts > auto make_v(size_t a, Ts... ts) { return vector< decltype(make_v< T >(ts...)) >(a, make_v< T >(ts...)); } template< typename T, typename V > typename enable_if< is_class< T >::value == 0 >::type fill_v(T &t, const V &v) { t = v; } template< typename T, typename V > typename enable_if< is_class< T >::value != 0 >::type fill_v(T &t, const V &v) { for(auto &e: t) fill_v(e, v); } template< typename F > struct FixPoint : F { FixPoint(F &&f) : F(forward< F >(f)) {} template< typename... Args > decltype(auto) operator()(Args &&... args) const { return F::operator()(*this, forward< Args >(args)...); } }; template< typename F > inline decltype(auto) MFP(F &&f) { return FixPoint< F >{forward< F >(f)}; } typedef pair< int, int > Point; enum : int { CREATEY, APPEARX, DELETEY }; struct Event { int y, idx, type; bool operator<(const Event &e) const { if(y == e.y) return (type < e.type); return (y < e.y); } }; struct BinaryIndexedTree { vector< int > data; BinaryIndexedTree(int sz) { data.assign(sz + 2, 0); } int query(int k) { int ret = 0; for(++k; k > 0; k -= k & -k) ret += data[k]; return (ret); } void update(int k, int x) { for(++k; k < data.size(); k += k & -k) data[k] += x; } }; struct SegmentTree { vector< int > isnew; int sz; SegmentTree(int n) { sz = 1; while(sz < n) sz <<= 1; isnew.assign(2 * sz - 1, false); } inline void AddF(const int a, const int b, int k, int l, int r) { if(a >= r || b <= l) { return; } else if(a <= l && r <= b) { isnew[k] = true; } else { AddF(a, b, 2 * k + 1, l, (l + r) >> 1); AddF(a, b, 2 * k + 2, (l + r) >> 1, r); } } inline void DelF(const int a, const int b, int k, int l, int r) { if(a >= r || b <= l) { return; } else if(a <= l && r <= a + 1) { isnew[k] = false; } else { if(isnew[k]) { isnew[2 * k + 1] = true; isnew[2 * k + 2] = true; isnew[k] = false; } DelF(a, b, 2 * k + 1, l, (l + r) >> 1); DelF(a, b, 2 * k + 2, (l + r) >> 1, r); } } inline void AddF(int a, int b) { AddF(a, b, 0, 0, sz); } inline void DelF(const int x) { DelF(x, x + 1, 0, 0, sz); } inline bool CheckF(int k) { k += sz - 1; if(isnew[k]) return (true); while(k > 0) { k = (k - 1) >> 1; if(isnew[k]) return (true); } while(k > 0); return (false); } }; struct UnionFind { vector< int > data; UnionFind() {}; inline int Make() { data.push_back(-1); return (data.size() - 1); } inline bool Unite(int a, int b) { a = Find(a), b = Find(b); if(a == b) return (false); if(data[a] > data[b]) swap(a, b); data[a] += data[b]; data[b] = a; return (true); } inline int Find(int k) { if(data[k] < 0) return (k); else return (data[k] = Find(data[k])); } }; SegmentTree *tree; UnionFind *uf; BinaryIndexedTree *bit; int decode[200020]; int Evaluate(int x) { if(tree->CheckF(x)) { tree->DelF(x); return (uf->Make()); } else { return (decode[x]); } } int Press(int *A, int sz1, int *B, int sz2) { vector< int > nums; copy(A, A + sz1, back_inserter(nums)); copy(B, B + sz2, back_inserter(nums)); nums.push_back(-1); sort(nums.begin(), nums.end()); nums.erase(unique(nums.begin(), nums.end()), nums.end()); transform(A, A + sz1, A, [&](int x) { return (lower_bound(nums.begin(), nums.end(), x) - nums.begin()); }); transform(B, B + sz2, B, [&](int x) { return (lower_bound(nums.begin(), nums.end(), x) - nums.begin()); }); return (nums.size()); } auto tap() { int N, M; cin >> N >> M; vector< tuple< int, int, int, int > > seg; { vector< vector< pair< int, int > > > es(200005); for(int i = 0; i < N; i++) { int a, b, c; cin >> a >> b >> c; es[a].emplace_back(b, c); } for(int i = 0; i < es.size(); i++) { sort(begin(es[i]), end(es[i])); int nxt = -1; for(auto&[l, r]: es[i]) { if(nxt < l) { if(~nxt) { get< 2 >(seg.back()) = nxt; } seg.emplace_back(l, i, r, i); } nxt = max(nxt, r); } } } { vector< vector< pair< int, int > > > es(200005); for(int i = 0; i < M; i++) { int a, b, c; cin >> a >> b >> c; es[a].emplace_back(b, c); } for(int i = 0; i < es.size(); i++) { sort(begin(es[i]), end(es[i])); int nxt = -1; for(auto&[l, r]: es[i]) { if(nxt < l) { if(~nxt) { get< 3 >(seg.back()) = nxt; } seg.emplace_back(i, l, i, r); } nxt = max(nxt, r); } } } return seg; } int main() { auto vs = tap(); int A[200005], B[200005], C[200005], D[200005]; int H = inf, W = inf, N = 0; for(auto&[a, b, c, d]: vs) { A[N] = a; B[N] = b; C[N] = c; D[N] = d; ++N; } A[N] = 0; B[N] = 0; C[N] = W; D[N] = 0; ++N; A[N] = 0; B[N] = 0; C[N] = 0; D[N] = H; ++N; A[N] = W; B[N] = 0; C[N] = W; D[N] = H; ++N; A[N] = 0; B[N] = H; C[N] = W; D[N] = H; ++N; W = Press(A, N, C, N); vector< Event > Events; for(int i = 0; i < N; i++) { if(A[i] == C[i]) { Events.push_back((Event) {B[i], i, CREATEY}); Events.push_back((Event) {D[i], i, DELETEY}); } else { Events.push_back((Event) {B[i], i, APPEARX}); } } sort(Events.begin(), Events.end()); set< int > pool; uf = new UnionFind(); tree = new SegmentTree(W); bit = new BinaryIndexedTree(W); pool.insert(0); decode[0] = uf->Make(); long long int sz = 0; for(int i = 0; i < Events.size(); i++) { const Event &e = Events[i]; const int &x = A[e.idx]; if(e.type == CREATEY) { int L = *--pool.lower_bound(x); decode[x] = Evaluate(x); decode[L] = Evaluate(L); decode[x] = decode[L]; pool.insert(x); bit->update(x, 1); } else if(e.type == DELETEY) { int L = *--pool.lower_bound(x); decode[x] = Evaluate(x); decode[L] = Evaluate(L); if(uf->Unite(decode[x], decode[L])) --sz; pool.erase(x); bit->update(x, -1); } else { int x2 = C[e.idx]; int RSQ = bit->query(x2) - bit->query(x - 1) - 1; if(RSQ > 0) { sz += RSQ; tree->AddF(x, *--pool.upper_bound(x2)); } } } cout << sz - 1 << "\n"; }