#include #include #define rep(i,n) for(int i=0;i P; template ostream& operator<<(ostream& os, const static_modint& a) {os << a.val(); return os;} template ostream& operator<<(ostream& os, const dynamic_modint& a) {os << a.val(); return os;} template istream& operator>>(istream& is, static_modint& a) {long long x; is >> x; a = x; return is;} template istream& operator>>(istream& is, dynamic_modint& a) {long long x; is >> x; a = x; return is;} template istream& operator>>(istream& is, vector& v){int n = v.size(); assert(n > 0); rep(i, n) is >> v[i]; return is;} template ostream& operator<<(ostream& os, const pair& p){os << p.first << ' ' << p.second; return os;} template ostream& operator<<(ostream& os, const vector& v){int n = v.size(); rep(i, n) os << v[i] << (i == n - 1 ? "\n" : " "); return os;} template ostream& operator<<(ostream& os, const vector>& v){int n = v.size(); rep(i, n) os << v[i] << (i == n - 1 ? "\n" : ""); return os;} template ostream& operator<<(ostream& os, const set& se){for(T x : se) os << x << " "; os << "\n"; return os;} template ostream& operator<<(ostream& os, const unordered_set& se){for(T x : se) os << x << " "; os << "\n"; return os;} template ostream& operator<<(ostream& os, const atcoder::segtree& seg){int n = seg.max_right(0, [](S){return true;}); rep(i, n) os << seg.get(i) << (i == n - 1 ? "\n" : " "); return os;} template ostream& operator<<(ostream& os, const atcoder::lazy_segtree& seg){int n = seg.max_right(0, [](S){return true;}); rep(i, n) os << seg.get(i) << (i == n - 1 ? "\n" : " "); return os;} template void chmin(T& a, T b){a = min(a, b);} template void chmax(T& a, T b){a = max(a, b);} // zeta & mobius transform // https://nyaannyaan.github.io/library/set-function/zeta-mobius-transform.hpp template void superset_zeta_transform(vector& f) { int n = f.size(); assert((n & (n - 1)) == 0); for (int i = 1; i < n; i <<= 1) { for (int j = 0; j < n; j++) { if ((j & i) == 0) { f[j] += f[j | i]; } } } } template void superset_mobius_transform(vector& f) { int n = f.size(); assert((n & (n - 1)) == 0); for (int i = 1; i < n; i <<= 1) { for (int j = 0; j < n; j++) { if ((j & i) == 0) { f[j] -= f[j | i]; } } } } template void subset_zeta_transform(vector& f) { int n = f.size(); assert((n & (n - 1)) == 0); for (int i = 1; i < n; i <<= 1) { for (int j = 0; j < n; j++) { if ((j & i) == 0) { f[j | i] += f[j]; } } } } template void subset_mobius_transform(vector& f) { int n = f.size(); assert((n & (n - 1)) == 0); for (int i = 1; i < n; i <<= 1) { for (int j = 0; j < n; j++) { if ((j & i) == 0) { f[j | i] -= f[j]; } } } } using mint = modint998244353; int main(){ int n, m, l; cin >> n >> m >> l; vector f(1 << l); f[0] = 1; vector> cnt(m, vector(l)); rep(i, n){ int a, b; cin >> a >> b; a--; b--; cnt[a][b - m]++; } rep(i, m){ vector g(1 << l); g[0] = 1; rep(j, l){ mint coeff = (mint(2).pow(cnt[i][j]) - 1); vector g_old(1 << l); swap(g, g_old); rep(x, (1 << l)){ g[x] += g_old[x]; g[x | (1 << j)] += g_old[x] * coeff; } } g[0] = 0; subset_zeta_transform(f); subset_zeta_transform(g); vector h(1 << l); rep(x, (1 << l)) h[x] += f[x] * g[x]; subset_mobius_transform(h); swap(f, h); } cout << f[(1 << l) - 1]; return 0; }