#pragma GCC optimize ("O3") #include "bits/stdc++.h" using namespace std; using ll = long long int; #define debugos cout #define debug(v) {printf("L%d %s > ",__LINE__,#v);debugos<<(v)< ",__LINE__,#v);for(auto e:(v)){debugos< ",__LINE__,#m);for(int x=0;x<(w);x++){debugos<<(m)[x]<<" ";}debugos<\n",__LINE__,#m);for(int y=0;y<(h);y++){for(int x=0;x<(w);x++){debugos<<(m)[y][x]<<" ";}debugos< f) { if (!assertion) { cerr << "assertion fault:" << endl; f(); abort(); } } template inline ostream& operator <<(ostream &o, const pair p) { o << '(' << p.first << ':' << p.second << ')'; return o; } template inline ostream& _ostream_vecprint(ostream& os, const Vec& a) { os << '['; for (const auto& e : a) os << ' ' << e << ' '; os << ']'; return os; } template inline ostream& operator<<(ostream& o, const vector& v) { return _ostream_vecprint(o, v); } template inline ostream& operator<<(ostream& o, const array& v) { return _ostream_vecprint(o, v); } template inline T& chmax(T& to, const T& val) { return to = max(to, val); } template inline T& chmin(T& to, const T& val) { return to = min(to, val); } void bye(string s, int code = 0) { cout << s << endl; exit(code); } mt19937_64 randdev(8901016); template::value>::type* = nullptr> inline T rand(T l, T h, Random& rand = randdev) { return uniform_int_distribution(l, h)(rand); } template::value>::type* = nullptr> inline T rand(T l, T h, Random& rand = randdev) { return uniform_real_distribution(l, h)(rand); } #if defined(_WIN32) || defined(_WIN64) #define getchar_unlocked _getchar_nolock #define putchar_unlocked _putchar_nolock #elif defined(__GNUC__) #else #define getchar_unlocked getchar #define putchar_unlocked putchar #endif namespace { #define isvisiblechar(c) (0x21<=(c)&&(c)<=0x7E) class MaiScanner { public: template void input_integer(T& var) noexcept { var = 0; T sign = 1; int cc = getchar_unlocked(); for (; cc < '0' || '9' < cc; cc = getchar_unlocked()) if (cc == '-') sign = -1; for (; '0' <= cc && cc <= '9'; cc = getchar_unlocked()) var = (var << 3) + (var << 1) + cc - '0'; var = var * sign; } inline int c() noexcept { return getchar_unlocked(); } inline MaiScanner& operator>>(int& var) noexcept { input_integer(var); return *this; } inline MaiScanner& operator>>(long long& var) noexcept { input_integer(var); return *this; } inline MaiScanner& operator>>(string& var) { int cc = getchar_unlocked(); for (; !isvisiblechar(cc); cc = getchar_unlocked()); for (; isvisiblechar(cc); cc = getchar_unlocked()) var.push_back(cc); return *this; } template inline void in(IT begin, IT end) { for (auto it = begin; it != end; ++it) *this >> *it; } }; class MaiPrinter { public: template void output_integer(T var) noexcept { if (var == 0) { putchar_unlocked('0'); return; } if (var < 0) putchar_unlocked('-'), var = -var; char stack[32]; int stack_p = 0; while (var) stack[stack_p++] = '0' + (var % 10), var /= 10; while (stack_p) putchar_unlocked(stack[--stack_p]); } inline MaiPrinter& operator<<(char c) noexcept { putchar_unlocked(c); return *this; } inline MaiPrinter& operator<<(int var) noexcept { output_integer(var); return *this; } inline MaiPrinter& operator<<(long long var) noexcept { output_integer(var); return *this; } inline MaiPrinter& operator<<(char* str_p) noexcept { while (*str_p) putchar_unlocked(*(str_p++)); return *this; } inline MaiPrinter& operator<<(const string& str) { const char* p = str.c_str(); const char* l = p + str.size(); while (p < l) putchar_unlocked(*p++); return *this; } template void join(IT begin, IT end, char sep = ' ') { for (bool b = 0; begin != end; ++begin, b = 1) b ? *this << sep << *begin : *this << *begin; } }; } MaiScanner scanner; MaiPrinter printer; template //using T = ll; class SegmentTree { public: struct IdxVal { int index; T value; inline IdxVal(int _i = 0, const T& _v = T()) :index(_i), value(_v) { } inline void set(int i, const T& v) { index = i; value = v; } inline bool operator<(const T& t) const { return value < t; } inline bool operator==(const T& t) const { return value == t; } inline bool operator<(const IdxVal& iv) const { return value < iv.value || (value == iv.value && index < iv.index); } inline bool operator==(const IdxVal& iv) const { return index == iv.index && value == iv.value; } }; enum struct Cmd { None, Add, Flat }; struct Node { T sum, lazyValue; IdxVal max; Cmd lazyType; }; private: const int size_; vector leaf_data_; vector node_data_; T* leaf_; Node* node_; void _resize() { leaf_data_.resize(size_); node_data_.resize(size_ - 1); leaf_ = &leaf_data_[0]; node_ = &node_data_[0] - 1; } void _init() { for (int i = size_ - 1; 0 < i; --i) { int c = i << 1; if (size_ <= c) node_[i].max.set(c - size_, leaf_[c - size_]); else node_[i].max = node_[c].max; node_[i].lazyType = Cmd::None; node_[i].sum = node_[i].lazyValue = 0; } } public: inline void fill(T val) { std::fill(leaf_data_.begin(), leaf_data_.end(), val); _init(); } SegmentTree(int _n) : size_([](int _n) {int s = 8; while (s < _n) s <<= 1; return s; }(_n)) { _resize(); _init(); } SegmentTree(int _n, const T& _fillVal) : size_([](int _n) {int s = 8; while (s < _n) s <<= 1; return s; }(_n)) { _resize(); fill(_fillVal); } private: // lazyを適応する(子を呼ぶ前に呼ぶ) // width: ptrが担当するnodeの範囲の大きさ(ptr = 1 ならば size_) inline void _applyDown(int ptr, int width) { width >>= 1; //if (size_ <= ptr) return; if (node_[ptr].lazyType == Cmd::Add) { int c = ptr << 1; if (size_ <= c) { leaf_[c - size_] += node_[ptr].lazyValue; leaf_[c + 1 - size_] += node_[ptr].lazyValue; } else { node_[c].sum += node_[ptr].lazyValue * width; node_[c].max.value += node_[ptr].lazyValue; node_[c].lazyValue += node_[ptr].lazyValue; if (node_[c].lazyType == Cmd::None) node_[c].lazyType = Cmd::Add; node_[c + 1].sum += node_[ptr].lazyValue * width; node_[c + 1].max.value += node_[ptr].lazyValue; node_[c + 1].lazyValue += node_[ptr].lazyValue; if (node_[c + 1].lazyType == Cmd::None) node_[c + 1].lazyType = Cmd::Add; } node_[ptr].lazyType = Cmd::None; node_[ptr].lazyValue = 0; } else if (node_[ptr].lazyType == Cmd::Flat) { node_[ptr].lazyType = Cmd::None; int c = ptr << 1; if (size_ <= c) { leaf_[c - size_] = node_[ptr].lazyValue; leaf_[c + 1 - size_] = node_[ptr].lazyValue; } else { node_[c].sum = node_[ptr].lazyValue * width; node_[c].max.value = node_[ptr].lazyValue; node_[c].lazyValue += node_[ptr].lazyValue; node_[c].lazyType = Cmd::Flat; node_[c + 1].sum = node_[ptr].lazyValue * width; node_[c + 1].max.value = node_[ptr].lazyValue; node_[c + 1].lazyValue += node_[ptr].lazyValue; node_[c + 1].lazyType = Cmd::Flat; } node_[ptr].lazyType = Cmd::None; node_[ptr].lazyValue = 0; } } // lazyを適応する(子を呼んだ後に呼ぶ) inline void _applyUp(int ptr) { if (size_ <= ptr << 1) { int i = (ptr << 1) - size_; if (leaf_[i] < leaf_[i + 1]) node_[ptr].max.set(i + 1, leaf_[i + 1]); else node_[ptr].max.set(i, leaf_[i]); node_[ptr].sum = leaf_[i] + leaf_[i + 1]; } else { if (node_[(ptr << 1)].max.value < node_[(ptr << 1) + 1].max.value) node_[ptr].max = node_[(ptr << 1) + 1].max; else node_[ptr].max = node_[(ptr << 1)].max; node_[ptr].sum = node_[ptr << 1].sum + node_[(ptr << 1) + 1].sum; } } // _downward_applyDown(ptr1): leaf[ptr1]の値を正しくする. void _downward_applyDown(int ptr1, int width = 1) { if (1 < ptr1) _downward_applyDown(ptr1 >> 1, width << 1); _applyDown(ptr1, width); } void _setValueRange(int begin, int end, T val, int ptr, int rangebegin, int rangeend) { if (rangeend <= begin || end <= rangebegin) return; // note:範囲外 if (begin <= rangebegin && rangeend <= end) { if (size_ <= ptr) { leaf_[rangebegin] = val; } else { node_[ptr].sum = val * (rangeend - rangebegin); node_[ptr].max.value = val; node_[ptr].lazyType = Cmd::Flat; node_[ptr].lazyValue = val; } return; } _applyDown(ptr, rangeend - rangebegin); _setValueRange(begin, end, val, ptr << 1, rangebegin, (rangebegin + rangeend) >> 1); _setValueRange(begin, end, val, (ptr << 1) + 1, (rangebegin + rangeend) >> 1, rangeend); _applyUp(ptr); } void _addValueRange(int begin, int end, T val, int ptr, int rangebegin, int rangeend) { if (rangeend <= begin || end <= rangebegin) return; // note:範囲外 if (begin <= rangebegin && rangeend <= end) { if (size_ <= ptr) { leaf_[rangebegin] += val; } else { node_[ptr].sum += val * (rangeend - rangebegin); node_[ptr].max.value += val; node_[ptr].lazyValue += val; if (node_[ptr].lazyType == Cmd::None) node_[ptr].lazyType = Cmd::Add; } return; } _applyDown(ptr, rangeend - rangebegin); _addValueRange(begin, end, val, ptr << 1, rangebegin, (rangebegin + rangeend) >> 1); _addValueRange(begin, end, val, (ptr << 1) + 1, (rangebegin + rangeend) >> 1, rangeend); _applyUp(ptr); } T _getSumRange(int begin, int end, int ptr, int rangebegin, int rangeend) { if (rangeend <= begin || end <= rangebegin) return 0; // note:範囲外 if (begin <= rangebegin && rangeend <= end) { if (size_ <= ptr) return leaf_[rangebegin]; else return node_[ptr].sum; } _applyDown(ptr, rangeend - rangebegin); return _getSumRange(begin, end, ptr << 1, rangebegin, (rangebegin + rangeend) >> 1) + _getSumRange(begin, end, (ptr << 1) + 1, (rangebegin + rangeend) >> 1, rangeend); } IdxVal _getMaxRange(int begin, int end, int ptr, int rangebegin, int rangeend) { if (rangeend <= begin || end <= rangebegin) return IdxVal(-1); // note:範囲外 if (begin <= rangebegin && rangeend <= end) { if (size_ <= ptr) return IdxVal(rangebegin, leaf_[rangebegin]); else return node_[ptr].max; } _applyDown(ptr, rangeend - rangebegin); auto l = _getMaxRange(begin, end, ptr << 1, rangebegin, (rangebegin + rangeend) >> 1); auto r = _getMaxRange(begin, end, (ptr << 1) + 1, (rangebegin + rangeend) >> 1, rangeend); if (l.index == -1) return move(r); if (r.index == -1) return move(l); return l.value < r.value ? move(r) : move(l); } public: // [index] の値を求める inline T getValue(int index) { _downward_applyDown((index + size_) >> 1); return leaf_[index]; } // [index] の値を書き換える inline void setValue(int index, T val) { _downward_applyDown((index + size_) >> 1); T diff = val - leaf_[index]; leaf_[index] = val; for (int ptr = (index + size_); 1 < ptr;) { ptr >>= 1; node_[ptr].sum -= diff; _applyUp(ptr); } } // [index] の値に加算する inline void addValue(int index, T val) { _downward_applyDown((index + size_) >> 1); leaf_[index] += val; for (int ptr = (index + size_); 1 < ptr;) { ptr >>= 1; node_[ptr].sum += val; _applyUp(ptr); } } // 区間[begin,end)全てにvalをセットする inline void setValueRange(int begin, int end, T val) { _setValueRange(begin, end, val, 1, 0, size_); } // 区間[begin,end)に一様にvalを加算する inline void addValueRange(int begin, int end, T val) { _addValueRange(begin, end, val, 1, 0, size_); } // 区間[begin,end)の和を求める inline T getSumRange(int begin, int end) { return _getSumRange(begin, end, 1, 0, size_); } // 区間[begin,end)の最大値とその位置を求める inline IdxVal getMaxRange(int begin, int end) { return _getMaxRange(begin, end, 1, 0, size_); } }; int N; array abc[200010]; inline bool on(int i, int j) { return abc[i][0] > abc[j][0] && abc[i][1] > abc[j][1]; } int main() { map xzip, yzip; scanner >> N; repeat(i, N) { int a, b, c; scanner >> a >> b >> c; abc[i][0] = a; abc[i][1] = b; abc[i][2] = c; int t; xzip[a]; yzip[b]; } { int i; i = 0; for (auto& p : xzip) p.second = i++; i = 0; for (auto& p : yzip) p.second = i++; } repeat(i, N) { abc[i][0] = xzip[abc[i][0]]; abc[i][1] = yzip[abc[i][1]]; } // ll dp[2000]; // // repeat(i, N) { // dp[i] = abc[i][2]; // } // repeat(i, N) { // repeat(j, N) { // if (on(i, j)) { // dp[j] += dp[i]; // } // } // } // // printer << *max_element(dp, dp + N) << '\n'; sort(abc, abc + N, greater&>()); const ll yMax = yzip.size() + 10; SegmentTree segY(yMax); queue> qu; repeat(i, N) { ll lo = segY.getMaxRange(abc[i][1]+1, yMax).value; ll cu = segY.getValue(abc[i][1]); if (cu < lo + abc[i][2]) qu.emplace(abc[i][1], lo + abc[i][2]); if (i + 1 < N && abc[i][0] > abc[i + 1][0]) { while (!qu.empty()) { ll c = segY.getValue(qu.front().first); if (c < qu.front().second) segY.setValue(qu.front().first, qu.front().second); qu.pop(); } } } while (!qu.empty()) { ll c = segY.getValue(qu.front().first); if (c < qu.front().second) segY.setValue(qu.front().first, qu.front().second); qu.pop(); } ll best = segY.getMaxRange(0, yMax).value; printer << best << '\n'; return 0; }