#include #include #include #include constexpr int32_t MAX_POINTS = 45; using Point = std::tuple; template int32_t fastFind(const std::vector& sortedData, T val) { return std::distance(sortedData.begin(), std::lower_bound(sortedData.begin(), sortedData.end(), val)); } // Add all elements from rhs to lhs, but stop when there are MAX elements template void cappedConcat(std::vector& a, const std::vector& b) { if (a.size() >= MAX_POINTS) { return; } for (const auto& val : b) { a.push_back(val); if (a.size() >= MAX_POINTS) { return; } } } // Weird offline seg tree class SegTree { public: void insert(int32_t y, int32_t h) { points_.emplace_back(y, h); } void finalize() { ys_.reserve(points_.size()); for (auto [y, h] : points_) { ys_.push_back(y); } std::sort(ys_.begin(), ys_.end()); ys_.erase(std::unique(ys_.begin(), ys_.end()), ys_.end()); data_.resize(2 * ys_.size()); for (auto [y, h] : points_) { int32_t idx = fastFind(ys_, y); for (idx += ys_.size(); idx > 0; idx /= 2) { data_[idx].push_back(h); } } } std::vector query(int32_t y0, int32_t y1) const { int32_t l = fastFind(ys_, y0); int32_t r = std::distance(ys_.begin(), std::upper_bound(ys_.begin(), ys_.end(), y1)); std::vector heights; for (l += ys_.size(), r += ys_.size(); heights.size() < MAX_POINTS && l < r; l /= 2, r /= 2) { if (l & 1) { cappedConcat(heights, data_[l]); l++; } if (r & 1) { --r; cappedConcat(heights, data_[r]); } } return heights; } private: std::vector> points_; std::vector ys_; std::vector> data_; }; // Segment tree of Segment trees class SegSegTree { public: SegSegTree(const std::vector& points): n_(points.size()) { xs_.reserve(points.size()); for (auto [x, y, h] : points) { xs_.push_back(x); } std::sort(xs_.begin(), xs_.end()); xs_.erase(std::unique(xs_.begin(), xs_.end()), xs_.end()); segTrees_.resize(2 * xs_.size()); for (auto [x, y, h] : points) { insert(x, y, h); } for (auto& segTree : segTrees_) { segTree.finalize(); } } // return heights in this window std::vector query(int32_t x0, int32_t x1, int32_t y0, int32_t y1) const { int32_t l = fastFind(xs_, x0); int32_t r = std::distance(xs_.begin(), std::upper_bound(xs_.begin(), xs_.end(), x1)); std::vector heights; heights.reserve(MAX_POINTS); for (l += xs_.size(), r += xs_.size(); heights.size() < MAX_POINTS && l < r; l /= 2, r /= 2) { if (l & 1) { cappedConcat(heights, segTrees_[l].query(y0, y1)); l++; } if (r & 1) { --r; cappedConcat(heights, segTrees_[r].query(y0, y1)); } } return heights; } private: void insert(int32_t x, int32_t y, int32_t h) { int32_t idx = fastFind(xs_, x); for (idx += xs_.size(); idx > 0; idx /= 2) { segTrees_[idx].insert(y, h); } } int n_; std::vector xs_; std::vector segTrees_; }; int main() { int32_t n, q; std::cin >> n >> q; std::vector points; points.reserve(n); for (int32_t i = 0; i < n; ++i) { int32_t x, y, h; std::cin >> x >> y >> h; points.emplace_back(x, y, h); } std::sort(points.begin(), points.end()); SegSegTree tree(points); for (int32_t query_idx = 0; query_idx < q; ++query_idx) { int32_t x0, y0, x1, y1; std::cin >> x0 >> y0 >> x1 >> y1; std::vector heights = tree.query(x0, x1, y0, y1); std::sort(heights.begin(), heights.end()); bool has_triangle = false; for (int32_t i = 0; i + 2 < heights.size(); ++i) { has_triangle |= heights[i] + heights[i + 1] > heights[i + 2]; } std::cout << has_triangle << '\n'; } return 0; }