import std; const segSize = 1 << 20; int[][] seg; void update(int l, int r, int v, int time) { l += segSize; r += segSize; while (l < r) { if (l % 2 == 1) { seg[l][v] = time; l++; } l /= 2; if (r % 2 == 1) { seg[r - 1][v] = time; r--; } r /= 2; } } // 親の更新を子に伝搬する(親は更新されない) void propagate() { void rec(int ind) { // 子がいない if (ind * 2 >= seg.length) return; int lt = ind * 2; int rt = ind * 2 + 1; foreach (i; 0..3) { // Y, K, C seg[lt][i] = max(seg[lt][i], seg[ind][i]); seg[rt][i] = max(seg[rt][i], seg[ind][i]); } rec(lt); rec(rt); } rec(1); } alias Q = Tuple!(int, "l", int, "r", int, "t"); int toInt(string c) { if (c == "Y") return 0; if (c == "K") return 1; return 2; } void main() { int n, m; scan(n, m); seg = new int[][](segSize * 2, 3); Q[] qs; foreach (_; 0..m) { int l, r; string t; scan(l, r, t); qs ~= Q(l, r, toInt(t)); } int time = 1; foreach_reverse (q; qs) { update(q.l, q.r + 1, q.t, time++); } propagate(); auto ans = [0, 0, 0]; auto nodes = seg[segSize+1..$].take(n); foreach (e; nodes) { if (e.sum == 0) continue; // 饅頭がない ans[e.maxIndex]++; } writeln(ans.map!text.join(' ')); } void scan(T...)(ref T a) { string[] ss = readln.split; foreach (i, t; T) a[i] = ss[i].to!t; } T read(T=string)() { return readln.chomp.to!T; } T[] reads(T)() { return readln.split.to!(T[]); } alias readints = reads!int;