#include <bits/stdc++.h>
#pragma GCC target("avx2")
#pragma GCC optimization("O3")
#pragma GCC optimization("unroll-loops")
using namespace std;
template <class T>
using vec = std::vector<T>;
template <class T>
using vec2d = std::vector<std::vector<T>>;
template <class T>
vec2d<T> makeVec2d(const int n1, const int n2, const T initValue) {
  return vec2d<T>(n1, vec<T>(n2, initValue));
}
bool __hack = std::ios::sync_with_stdio(false);
auto __hack1 = cin.tie(nullptr);
struct Input {
  Input(istream &in) : in(&in) {}
  template <class T>
  T next() const {
    T x;
    *in >> x;
    return x;
  }
  int ni() const { return next<int>(); }
  istream *in;
};
Input in(cin);
class Output {
 private:
  ostream *out;
  template <typename T>
  void printSingle(const T &value) {
    *out << value;
  }

 public:
  Output(ostream &out) : out(&out) {}
  inline void print() {}
  template <typename T, typename... Ts>
  inline void print(const T &f, const Ts &...args) {
    printSingle(f);
    if (sizeof...(args) != 0) {
      *out << ' ';
      print(args...);
    }
  }
  template <typename... Ts>
  inline void println(const Ts &...args) {
    print(args...);
    *out << '\n';
  }
  template <typename... Ts>
  inline void operator()(const Ts &...args) {
    println(args...);
  }
};
Output out(cout);
namespace template_util {
constexpr int bytecount(uint64_t x) { return x ? 1 + bytecount(x >> 8) : 0; }
template <int N>
struct bytetype {};
template <uint64_t N>
struct minimal_uint : bytetype<bytecount(N)> {};
}  // namespace template_util
template <typename T>
static constexpr T wrap_pos(T a, T m) {
  return a < 0 ? a + m : a;
}
template <typename T>
static constexpr T mod(T a, T m) {
  return wrap_pos(a % m, m);
}
template <typename T>
static constexpr T inverse2(T a, T m) {
  return a <= 1 ? a : mod((1 - inverse2(m % a, a) * m) / a, m);
}
template <typename T>
static constexpr T inverse(T a, T m) {
  return inverse2(mod(a, m), m);
}
template <typename T, typename C, T Modulus>
class MR {
 private:
  struct tag_plus {};
  struct tag_minus {};
  struct tag_good {};
  static_assert(std::numeric_limits<C>::max() / Modulus / Modulus > 0,
                "compute type is too small");
  static_assert(Modulus < std::numeric_limits<T>::max() / 2,
                "storage type is too small");
  static constexpr T reduce(T value, tag_plus) {
    return value >= Modulus ? value - Modulus : value;
  }
  static constexpr T reduce(T value, tag_minus) {
    return value < 0 ? value + Modulus : value;
  }
  static constexpr T reduce(T value, tag_good) { return value; }

 public:
  T value;
  constexpr MR() : value(0) {}
  constexpr MR(C value) : value(reduce(value % Modulus, tag_minus())) {}
  template <typename tag_t>
  constexpr MR(T value, tag_t tag) : value(reduce(value, tag)) {}
  MR &operator=(C value) {
    this->value = reduce(value % Modulus, tag_minus());
    return *this;
  }
  constexpr MR operator*(MR b) const {
    return MR(C(value) * C(b.value) % Modulus, tag_good());
  }
  MR &operator+=(MR b) {
    value = reduce(value + b.value, tag_plus());
    return *this;
  }
  constexpr T get() const { return value; }
  constexpr MR inverse() const {
    return MR(::inverse(C(value), C(Modulus)), tag_good());
  }
  constexpr MR operator/(MR b) const { return *this * b.inverse(); }
};
template <typename T, typename C, T Modulus>
static inline std::ostream &operator<<(std::ostream &o, MR<T, C, Modulus> mr) {
  return o << mr.get();
}
template <class T>
vector<T> getFactorials(int n) {
  vector<T> res(n);
  res[0] = T(1);
  for (int i = 1; i < n; ++i) {
    res[i] = res[i - 1] * i;
  }
  return res;
}
template <class T>
inline T setBit(T mask, int bit) {
  assert(0 <= bit && bit < numeric_limits<T>::digits);
  return mask | ((T)(1) << bit);
}
template <class T>
inline bool getBit(T mask, int bit) {
  assert(0 <= bit && bit < numeric_limits<T>::digits);
  return mask & ((T)(1) << bit);
}
template <class T>
inline int bitCnt(T mask);
template <>
inline int bitCnt(int32_t mask) {
  return __builtin_popcount(mask);
}
struct Component {
  vec<int> vs;
  vec2d<MR<int, int64_t, 998244353>> dp;
  Component() {}
};
void solve(istream &inStream, ostream &outStream) {
  in = Input(inStream);
  out = Output(outStream);
  auto n = in.ni();
  auto m = in.ni();
  vec2d<pair<int, int>> events(n + 2);
  for (int i = 0; i < (n); ++i) {
    auto x = in.ni();
    auto y = in.ni() + 1;
    events[x].push_back({1, i});
    events[y].push_back({0, i});
  }
  vec2d<int> g(n);
  set<pair<int, int>> edges;
  for (int i = 0; i < (m); ++i) {
    auto x = in.ni() - 1;
    auto y = in.ni() - 1;
    edges.insert({x, y});
    edges.insert({y, x});
    g[x].push_back(y);
    g[y].push_back(x);
  }
  vec<int> u(n, 0);
  vec<int> vs;
  vec<Component> components;
  function<void(int)> dfs = [&](int v) {
    u[v] = true;
    vs.push_back(v);
    for (auto to : g[v]) {
      if (!u[to]) {
        dfs(to);
      }
    }
  };
  for (int i = 0; i < (n); ++i) {
    if (u[i]) continue;
    if (g[i].empty()) {
      continue;
    }
    vs.clear();
    dfs(i);
    Component cc;
    cc.vs = vs;
    int cn = (int)vs.size();
    cc.dp = makeVec2d<MR<int, int64_t, 998244353>>(1 << cn, cn + 1, 0);
    for (int mask = 0; mask < (1 << cn); ++mask) {
      bool good = true;
      for (int b1 = 0; b1 < (cn); ++b1) {
        if (getBit(mask, b1)) {
          for (int b2 = 0; b2 < (b1); ++b2) {
            if (getBit(mask, b2)) {
              if (edges.find({cc.vs[b1], cc.vs[b2]}) != edges.end()) {
                good = false;
                break;
              }
            }
          }
          if (!good) break;
        }
      }
      if (good) {
        cc.dp[mask][bitCnt(mask)] = 1;
      }
    };
    ;
    for (int j = 0; j < (cn); ++j) {
      for (int b = 0; b < (cn); ++b) {
        for (int mask = 0; mask < (1 << cn); ++mask) {
          if (!getBit(mask, b)) {
            cc.dp[setBit(mask, b)][j] += cc.dp[mask][j];
          }
        }
      }
    }
    components.push_back(cc);
  }
  auto fs = getFactorials<MR<int, int64_t, 998244353>>(n + 1);
  auto rfs = fs;
  for (int i = 0; i < (rfs.size()); ++i) {
    rfs[i] = MR<int, int64_t, 998244353>(1) / rfs[i];
  }
  auto C = [&](int n, int k) -> MR<int, int64_t, 998244353> {
    return fs[n] * rfs[k] * rfs[n - k];
  };
  unordered_set<int> presentIds;
  presentIds.reserve(1 << 16);
  presentIds.max_load_factor(0.25);
  MR<int, int64_t, 998244353> res = 0;
  for (int i = (1); i < (n + 1); ++i) {
    for (const auto &e : events[i]) {
      if (e.first == 0) {
        presentIds.erase(e.second);
      } else {
        presentIds.insert(e.second);
      }
    }
    vec<MR<int, int64_t, 998244353>> dp(2 * m + 1, 0);
    dp[0] = 1;
    int inC = 0;
    for (const auto &c : components) {
      int mask = 0;
      for (int i = 0; i < c.vs.size(); ++i) {
        if (presentIds.find(c.vs[i]) != presentIds.end()) {
          mask = setBit(mask, i);
        }
      }
      inC += bitCnt(mask);
      for (int j = (int)dp.size() - 1; j >= 0; --j) {
        for (int add = (1); add < (c.vs.size() + 1); ++add) {
          if (j + add < dp.size()) {
            dp[j + add] += dp[j] * c.dp[mask][add];
          }
        }
      }
    }
    for (int j = 0; j < (dp.size()); ++j) {
      if (presentIds.size() - inC >= i - j && i - j >= 0) {
        res += dp[j] * C(presentIds.size() - inC, i - j);
      }
    }
  }
  out(res);
}
int main() {
  solve(cin, cout);
  return 0;
}
