typedeflonglong ll; constint N = 2e3+10; constint P = 1e9+7;
int n, k, L; ll ans; int x[N], y[N], col[N]; int lshx[N], lshy[N], xcnt, ycnt; vector<int> idx[N], idy[N]; multiset<int> s[N]; int pos[N];
namespace segment_tree { structnode { int l, r; int max, lz; ll val, sum; } a[N*4]; voidassign(int ql, int qr, ll val, int i = 1); voidbuild(int l, int r, int i = 1); intquery(ll val, int i = 1); } usingnamespace segment_tree;
voidprework();
signedmain() { cin >> n >> k >> L; for(int i = 1; i <= n; i++) { cin >> x[i] >> y[i] >> col[i]; lshx[i] = ++x[i], lshy[i] = ++y[i]; } prework(); segment_tree::build(1, ycnt); for(int i = 1; i <= k; i++) s[i].insert(0), s[i].insert(ycnt+1); for(int i = 1; i <= xcnt; i++) { multiset<int> tmp; for(int j = 1; j <= k; j++) pos[j] = ycnt+1, tmp.insert(ycnt+1); for(int j = ycnt; j >= 1; j--) { for(auto z : idy[j]) if(x[z] >= i) { tmp.erase(tmp.find(pos[col[z]])); pos[col[z]] = j, tmp.insert(pos[col[z]]); s[col[z]].insert(j); } assign(j, j, lshy[*tmp.rbegin()]); } for(int j = xcnt; j >= i; j--) { (ans += (ll)(lshx[i]-lshx[i-1])%P * (lshx[j+1]-lshx[j])%P * (((ll)lshy[ycnt]*(L+1)%P-a[1].sum+P)%P)%P) %= P; for(auto z : idx[j]) { s[col[z]].erase(s[col[z]].find(y[z])); int pre = *--s[col[z]].upper_bound(y[z]); int nxt = *s[col[z]].upper_bound(y[z]); if(pre+1 <= y[z]) { int w = min(query(lshy[nxt])-1, y[z]); if(pre+1 <= w) assign(pre+1, w, lshy[nxt]); } } } } cout << ans << "\n"; }