#include class Dsu { public: Dsu() {} explicit Dsu(int n) : dat(n, -1), num_ccs_(n) {} int size() const { return std::size(dat); } int root(int v) { assert(0 <= v), assert(v < size()); return dat[v] < 0 ? v : dat[v] = root(dat[v]); } std::pair unite(int u, int v) { assert(0 <= u), assert(u < size()); assert(0 <= v), assert(v < size()); u = root(u), v = root(v); if (u == v) return {u, -1}; --num_ccs_; if (-dat[u] < -dat[v]) std::swap(u, v); dat[u] += dat[v]; dat[v] = u; return {u, v}; } bool same(int u, int v) { assert(0 <= u), assert(u < size()); assert(0 <= v), assert(v < size()); return root(u) == root(v); } int size(int v) { assert(0 <= v), assert(v < size()); return -dat[root(v)]; } int num_ccs() const { return num_ccs_; } private: std::vector dat; int num_ccs_; }; template class ModularInt { using M = ModularInt; public: static_assert(int(Modulus) >= 1, "Modulus must be in the range [1, 2^31)"); static constexpr int modulus() { return Modulus; } static M raw(uint32_t v) { return *reinterpret_cast(&v); } ModularInt() : v_(0) {} ModularInt(int64_t v) : v_((v %= Modulus) < 0 ? v + Modulus : v) {} template explicit operator T() const { return v_; } M& operator++() { return v_ = ++v_ == Modulus ? 0 : v_, *this; } M& operator--() { return --(v_ ? v_ : v_ = Modulus), *this; } M operator+() const { return *this; } M operator-() const { return raw(v_ ? Modulus - v_ : 0); } M& operator*=(M o) { return v_ = uint64_t(v_) * o.v_ % Modulus, *this; } M& operator/=(M o) { auto [inv, gcd] = extgcd(o.v_, Modulus); assert(gcd == 1); return *this *= inv; } M& operator+=(M o) { return v_ = int(v_ += o.v_ - Modulus) < 0 ? v_ + Modulus : v_, *this; } M& operator-=(M o) { return v_ = int(v_ -= o.v_) < 0 ? v_ + Modulus : v_, *this; } friend M operator++(M& a, int) { return std::exchange(a, ++M(a)); } friend M operator--(M& a, int) { return std::exchange(a, --M(a)); } friend M operator*(M a, M b) { return a *= b; } friend M operator/(M a, M b) { return a /= b; } friend M operator+(M a, M b) { return a += b; } friend M operator-(M a, M b) { return a -= b; } friend std::istream& operator>>(std::istream& is, M& x) { int64_t v; return is >> v, x = v, is; } friend std::ostream& operator<<(std::ostream& os, M x) { return os << x.v_; } friend bool operator==(M a, M b) { return a.v_ == b.v_; } friend bool operator!=(M a, M b) { return a.v_ != b.v_; } private: static std::array extgcd(int a, int b) { std::array x{1, 0}; while (b) std::swap(x[0] -= a / b * x[1], x[1]), std::swap(a %= b, b); return {x[0], a}; } uint32_t v_; }; #pragma region my_template struct Rep { struct I { int i; void operator++() { ++i; } int operator*() const { return i; } bool operator!=(I o) const { return i < *o; } }; const int l_, r_; Rep(int l, int r) : l_(l), r_(r) {} Rep(int n) : Rep(0, n) {} I begin() const { return {l_}; } I end() const { return {r_}; } }; struct Per { struct I { int i; void operator++() { --i; } int operator*() const { return i; } bool operator!=(I o) const { return i > *o; } }; const int l_, r_; Per(int l, int r) : l_(l), r_(r) {} Per(int n) : Per(0, n) {} I begin() const { return {r_ - 1}; } I end() const { return {l_ - 1}; } }; template struct Fix : private F { Fix(F f) : F(f) {} template decltype(auto) operator()(Args&&... args) const { return F::operator()(*this, std::forward(args)...); } }; template T scan() { T res; std::cin >> res; return res; } template bool chmin(T& a, U&& b) { return b < a ? a = std::forward(b), true : false; } template bool chmax(T& a, U&& b) { return a < b ? a = std::forward(b), true : false; } #ifndef LOCAL #define DUMP(...) void(0) template constexpr int OjLocal = OnlineJudge; #endif using namespace std; #define ALL(c) begin(c), end(c) #pragma endregion using Mint = ModularInt<998244353>; constexpr int N = 1 << 10; auto fact = [] { std::vector res(N + 1); res[0] = 1; for (int i = 1; i <= N; ++i) res[i] = i * res[i - 1]; return res; }(); auto ifact = [] { std::vector res(N + 1); res[N] = 1 / fact[N]; for (int i = N; i--;) res[i] = res[i + 1] * (i + 1); return res; }(); Mint binom(int n, int k) { assert(n <= N); return 0 <= k and k <= n ? fact[n] * ifact[k] * ifact[n - k] : 0; } Mint homo(int n, int k) { return n or k ? binom(n + k - 1, k) : 1; } auto minv = [] { std::vector res(N + 1); for (int i = 1; i <= N; ++i) res[i] = ifact[i] * fact[i - 1]; return res; }(); template <> Mint& Mint::operator/=(Mint o) { assert(o.v_); return *this *= o.v_ <= N ? minv[o.v_] : extgcd(o.v_, modulus())[0]; } template T determinant(vector> a) { T det = 1; for (int j = 0, r = 0, n = a.size(); j < n; ++j, ++r) { for (int i = r + 1; i < n; ++i) if (!(a[i][j] == 0)) { swap(a[r], a[i]), det = -det; break; } if (a[r][j] == 0) return 0; det *= a[r][j]; T inv = 1 / a[r][j]; for (auto&& e : a[r]) e *= inv; for (int i = 0; i < n; ++i) if (i != r and !(a[i][j] == 0)) for (int k = n; k-- > j;) a[i][k] -= a[r][k] * a[i][j]; } return det; } int main() { cin.tie(nullptr)->sync_with_stdio(false); cout << fixed << setprecision(20); int n = scan(); vector g(n, vector(n)); for (int m = scan(); m--;) { int i = scan() - 1; int j = scan() - 1; g[i][j] = true; g[j][i] = true; } Dsu d(n); for (int j : Rep(n)) for (int i : Rep(j)) if (g[i][j]) d.unite(i, j); Mint ans = 1; for (int i : Rep(n)) if (d.root(i) == i) { vector idx; for (int j : Rep(n)) if (d.same(i, j)) idx.push_back(j); vector mat(size(idx), vector(size(idx))); for (int y : Rep(size(idx))) for (int x : Rep(y)) { int u = idx[x]; int v = idx[y]; if (g[u][v]) { --mat[x][y]; --mat[y][x]; ++mat[x][x]; ++mat[y][y]; } } mat.resize(size(idx) - 1); for (auto&& e : mat) e.resize(size(idx) - 1); ans *= determinant(mat); } if (d.num_ccs() == 1) { cout << "0\n"; } else { vector a; for (int i : Rep(n)) if (d.root(i) == i) a.push_back(d.size(i)); sort(ALL(a), greater{}); while (a[1] != a.back()) a.pop_back(); Mint coeff; if (a[0] == a[1]) { for (int x : a) { for (int y : a) coeff += x * y; coeff -= x * x; } coeff /= 2; } else { coeff += a[0] * accumulate(1 + ALL(a), 0); } ans *= coeff; int opt = n * n; for (int i : Rep(n)) if (d.root(i) == i) opt -= d.size(i) * d.size(i); opt -= 2 * a[0] * a[1]; cout << opt << '\n'; } cout << ans << '\n'; }