#include "bits/stdc++.h" using namespace std; #define FOR(i,j,k) for(int (i)=(j);(i)<(int)(k);++(i)) #define rep(i,j) FOR(i,0,j) #define each(x,y) for(auto &(x):(y)) #define mp make_pair #define all(x) (x).begin(),(x).end() #define debug(x) cout<<#x<<": "<<(x)< pii; typedef vector vi; typedef vector vll; template void resize(vector &a, int n) { a.reserve(n); a.resize(n); } ll N; int Q, M; vll lrVal, X, L, R; const int nTeam = 5; const ll MOD = (ll)1e18 + 9; // 1.5*10^5 * 10^13 = 1.5*10^18 < LLNG_MAX/2 なので // SegTreeの中ではオーバーフローしない。bonusを得た場合だけ加算の時に気をつければ良い。 class SegTreeLazy { public: int dataSize; vll value, lazyValue; vi lazyClear; SegTreeLazy(int n) { dataSize = 1; while (dataSize < n)dataSize *= 2; int vectorSize = 2 * dataSize - 1; resize(value, vectorSize); resize(lazyValue, vectorSize); resize(lazyClear, vectorSize); } void propagate(int index, int curL, int curR) { if (lazyClear[index])value[index] = 0; // dataSizeは2^k >= M を満たす最小の2^kであるから、 // 2^k > M かもしれない。 // よって、lrValの範囲外参照に注意 value[index] += lazyValue[index] * (lrVal[min(M - 1, curR)] - lrVal[min(M - 1, curL)]); if (curR - curL > 1) { int indexL = index * 2 + 1, indexR = index * 2 + 2; if (lazyClear[index]) lazyValue[indexL] = lazyValue[indexR] = 0; for (int indexC : {indexL, indexR}) { lazyClear[indexC] |= lazyClear[index]; lazyValue[indexC] += lazyValue[index]; } } lazyValue[index] = lazyClear[index] = 0; } // [curL, curR) を評価する void update(int index, int curL, int curR, int givenL, int givenR, int isAdd) { // 評価し終わったら、必ずlazyClear[index]とlazyValue[index]はデフォルト値になっているはず // 範囲外であろうとpropagateは必ず呼ぶ。 propagate(index, curL, curR); // 範囲外 if (curR <= givenL || givenR <= curL)return; if (givenL <= curL && curR <= givenR) { // 直接書き換えないで、lazyClear,lazyValueを更新してからpropagateを呼ぶ if (isAdd) { lazyValue[index]++; } else { lazyClear[index] = 1; lazyValue[index] = 0; } propagate(index, curL, curR); } else { int mid = (curL + curR) / 2; update(index * 2 + 1, curL, mid, givenL, givenR, isAdd); update(index * 2 + 2, mid, curR, givenL, givenR, isAdd); value[index] = value[index * 2 + 1] + value[index * 2 + 2]; } } void update(int l, int r, int isAdd) { update(0, 0, dataSize, l, r, isAdd); } ll query(int l, int r) { return query(0, 0, dataSize, l, r); } ll query(int index, int curL, int curR, int givenL, int givenR) { // 範囲外 if (curR <= givenL || givenR <= curL)return 0; propagate(index, curL, curR); if (givenL <= curL && curR <= givenR) { return value[index]; } else { int mid = (curL + curR) / 2; ll resL = query(index * 2 + 1, curL, mid, givenL, givenR); ll resR = query(index * 2 + 2, mid, curR, givenL, givenR); return resL + resR; } } }; int main() { cin >> N >> Q; resize(X, Q); resize(L, Q); resize(R, Q); // 区間の座標圧縮 rep(i, Q) { scanf("%lld%lld%lld", &X[i], &L[i], &R[i]); for (ll val : {L[i], L[i] + 1, R[i], R[i] + 1}) lrVal.push_back(val); } lrVal.shrink_to_fit(); sort(all(lrVal)); lrVal.erase(unique(all(lrVal)), lrVal.end()); rep(i, Q) { L[i] = (int)(lower_bound(all(lrVal), L[i]) - lrVal.begin()); R[i] = (int)(lower_bound(all(lrVal), R[i]) - lrVal.begin()); } M = sz(lrVal); vector segTree(nTeam, SegTreeLazy(M)); vll ans(nTeam); rep(q, Q) { int x = (int)X[q], l = (int)L[q], r = (int)R[q]; // チームのインデックスを0-Originにしておく --x; // [l,r] => [l, r) r++; if (x == -1) { vector> bonusAndTeam(nTeam); rep(i, nTeam) { bonusAndTeam[i] = mp(segTree[i].query(l, r), i); } sort(bonusAndTeam.rbegin(), bonusAndTeam.rend()); ll bonus = bonusAndTeam[0].first; // トップタイがいたら、ボーナス無し if (bonus != bonusAndTeam[1].first) { (ans[bonusAndTeam[0].second] += bonus) %= MOD; } } else { rep(i, nTeam) { if (i == x)segTree[i].update(l, r, 1); else segTree[i].update(l, r, 0); } } } rep(i, nTeam) { (ans[i] += segTree[i].query(0, M)) %= MOD; printf("%lld%c", ans[i], i != nTeam - 1 ? ' ' : '\n'); } }