結果
問題 | No.2985 May Count Induced C4 Subgraphs |
ユーザー |
|
提出日時 | 2024-12-10 15:13:24 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 4,978 ms / 5,000 ms |
コード長 | 36,642 bytes |
コンパイル時間 | 4,990 ms |
コンパイル使用メモリ | 321,920 KB |
最終ジャッジ日時 | 2025-02-26 11:49:05 |
ジャッジサーバーID (参考情報) |
judge3 / judge2 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | AC * 22 |
ソースコード
/*** date : 2024-12-10 15:13:18* author : Nyaan*/#define NDEBUGusing namespace std;// intrinstic#include <immintrin.h>#include <algorithm>#include <array>#include <bitset>#include <cassert>#include <cctype>#include <cfenv>#include <cfloat>#include <chrono>#include <cinttypes>#include <climits>#include <cmath>#include <complex>#include <cstdarg>#include <cstddef>#include <cstdint>#include <cstdio>#include <cstdlib>#include <cstring>#include <deque>#include <fstream>#include <functional>#include <initializer_list>#include <iomanip>#include <ios>#include <iostream>#include <istream>#include <iterator>#include <limits>#include <list>#include <map>#include <memory>#include <new>#include <numeric>#include <ostream>#include <queue>#include <random>#include <set>#include <sstream>#include <stack>#include <streambuf>#include <string>#include <tr2/dynamic_bitset>#include <tuple>#include <type_traits>#include <typeinfo>#include <unordered_map>#include <unordered_set>#include <utility>#include <vector>// utilitynamespace Nyaan {using ll = long long;using i64 = long long;using u64 = unsigned long long;using i128 = __int128_t;using u128 = __uint128_t;template <typename T>using V = vector<T>;template <typename T>using VV = vector<vector<T>>;using vi = vector<int>;using vl = vector<long long>;using vd = V<double>;using vs = V<string>;using vvi = vector<vector<int>>;using vvl = vector<vector<long long>>;template <typename T>using minpq = priority_queue<T, vector<T>, greater<T>>;template <typename T, typename U>struct P : pair<T, U> {template <typename... Args>constexpr P(Args... args) : pair<T, U>(args...) {}using pair<T, U>::first;using pair<T, U>::second;P &operator+=(const P &r) {first += r.first;second += r.second;return *this;}P &operator-=(const P &r) {first -= r.first;second -= r.second;return *this;}P &operator*=(const P &r) {first *= r.first;second *= r.second;return *this;}template <typename S>P &operator*=(const S &r) {first *= r, second *= r;return *this;}P operator+(const P &r) const { return P(*this) += r; }P operator-(const P &r) const { return P(*this) -= r; }P operator*(const P &r) const { return P(*this) *= r; }template <typename S>P operator*(const S &r) const {return P(*this) *= r;}P operator-() const { return P{-first, -second}; }};using pl = P<ll, ll>;using pi = P<int, int>;using vp = V<pl>;constexpr int inf = 1001001001;constexpr long long infLL = 4004004004004004004LL;template <typename T>int sz(const T &t) {return t.size();}template <typename T, typename U>inline bool amin(T &x, U y) {return (y < x) ? (x = y, true) : false;}template <typename T, typename U>inline bool amax(T &x, U y) {return (x < y) ? (x = y, true) : false;}template <typename T>inline T Max(const vector<T> &v) {return *max_element(begin(v), end(v));}template <typename T>inline T Min(const vector<T> &v) {return *min_element(begin(v), end(v));}template <typename T>inline long long Sum(const vector<T> &v) {return accumulate(begin(v), end(v), 0LL);}template <typename T>int lb(const vector<T> &v, const T &a) {return lower_bound(begin(v), end(v), a) - begin(v);}template <typename T>int ub(const vector<T> &v, const T &a) {return upper_bound(begin(v), end(v), a) - begin(v);}constexpr long long TEN(int n) {long long ret = 1, x = 10;for (; n; x *= x, n >>= 1) ret *= (n & 1 ? x : 1);return ret;}template <typename T, typename U>pair<T, U> mkp(const T &t, const U &u) {return make_pair(t, u);}template <typename T>vector<T> mkrui(const vector<T> &v, bool rev = false) {vector<T> ret(v.size() + 1);if (rev) {for (int i = int(v.size()) - 1; i >= 0; i--) ret[i] = v[i] + ret[i + 1];} else {for (int i = 0; i < int(v.size()); i++) ret[i + 1] = ret[i] + v[i];}return ret;};template <typename T>vector<T> mkuni(const vector<T> &v) {vector<T> ret(v);sort(ret.begin(), ret.end());ret.erase(unique(ret.begin(), ret.end()), ret.end());return ret;}template <typename F>vector<int> mkord(int N, F f) {vector<int> ord(N);iota(begin(ord), end(ord), 0);sort(begin(ord), end(ord), f);return ord;}template <typename T>vector<int> mkinv(vector<T> &v) {int max_val = *max_element(begin(v), end(v));vector<int> inv(max_val + 1, -1);for (int i = 0; i < (int)v.size(); i++) inv[v[i]] = i;return inv;}vector<int> mkiota(int n) {vector<int> ret(n);iota(begin(ret), end(ret), 0);return ret;}template <typename T>T mkrev(const T &v) {T w{v};reverse(begin(w), end(w));return w;}template <typename T>bool nxp(T &v) {return next_permutation(begin(v), end(v));}// 返り値の型は入力の T に依存// i 要素目 : [0, a[i])template <typename T>vector<vector<T>> product(const vector<T> &a) {vector<vector<T>> ret;vector<T> v;auto dfs = [&](auto rc, int i) -> void {if (i == (int)a.size()) {ret.push_back(v);return;}for (int j = 0; j < a[i]; j++) v.push_back(j), rc(rc, i + 1), v.pop_back();};dfs(dfs, 0);return ret;}// F : function(void(T&)), mod を取る操作// T : 整数型のときはオーバーフローに注意するtemplate <typename T>T Power(T a, long long n, const T &I, const function<void(T &)> &f) {T res = I;for (; n; f(a = a * a), n >>= 1) {if (n & 1) f(res = res * a);}return res;}// T : 整数型のときはオーバーフローに注意するtemplate <typename T>T Power(T a, long long n, const T &I = T{1}) {return Power(a, n, I, function<void(T &)>{[](T &) -> void {}});}template <typename T>T Rev(const T &v) {T res = v;reverse(begin(res), end(res));return res;}template <typename T>vector<T> Transpose(const vector<T> &v) {using U = typename T::value_type;if(v.empty()) return {};int H = v.size(), W = v[0].size();vector res(W, T(H, U{}));for (int i = 0; i < H; i++) {for (int j = 0; j < W; j++) {res[j][i] = v[i][j];}}return res;}template <typename T>vector<T> Rotate(const vector<T> &v, int clockwise = true) {using U = typename T::value_type;int H = v.size(), W = v[0].size();vector res(W, T(H, U{}));for (int i = 0; i < H; i++) {for (int j = 0; j < W; j++) {if (clockwise) {res[W - 1 - j][i] = v[i][j];} else {res[j][H - 1 - i] = v[i][j];}}}return res;}} // namespace Nyaan// bit operationnamespace Nyaan {__attribute__((target("popcnt"))) inline int popcnt(const u64 &a) {return __builtin_popcountll(a);}inline int lsb(const u64 &a) { return a ? __builtin_ctzll(a) : 64; }inline int ctz(const u64 &a) { return a ? __builtin_ctzll(a) : 64; }inline int msb(const u64 &a) { return a ? 63 - __builtin_clzll(a) : -1; }template <typename T>inline int gbit(const T &a, int i) {return (a >> i) & 1;}template <typename T>inline void sbit(T &a, int i, bool b) {if (gbit(a, i) != b) a ^= T(1) << i;}constexpr long long PW(int n) { return 1LL << n; }constexpr long long MSK(int n) { return (1LL << n) - 1; }} // namespace Nyaan// inoutnamespace Nyaan {template <typename T, typename U>ostream &operator<<(ostream &os, const pair<T, U> &p) {os << p.first << " " << p.second;return os;}template <typename T, typename U>istream &operator>>(istream &is, pair<T, U> &p) {is >> p.first >> p.second;return is;}template <typename T>ostream &operator<<(ostream &os, const vector<T> &v) {int s = (int)v.size();for (int i = 0; i < s; i++) os << (i ? " " : "") << v[i];return os;}template <typename T>istream &operator>>(istream &is, vector<T> &v) {for (auto &x : v) is >> x;return is;}istream &operator>>(istream &is, __int128_t &x) {string S;is >> S;x = 0;int flag = 0;for (auto &c : S) {if (c == '-') {flag = true;continue;}x *= 10;x += c - '0';}if (flag) x = -x;return is;}istream &operator>>(istream &is, __uint128_t &x) {string S;is >> S;x = 0;for (auto &c : S) {x *= 10;x += c - '0';}return is;}ostream &operator<<(ostream &os, __int128_t x) {if (x == 0) return os << 0;if (x < 0) os << '-', x = -x;string S;while (x) S.push_back('0' + x % 10), x /= 10;reverse(begin(S), end(S));return os << S;}ostream &operator<<(ostream &os, __uint128_t x) {if (x == 0) return os << 0;string S;while (x) S.push_back('0' + x % 10), x /= 10;reverse(begin(S), end(S));return os << S;}void in() {}template <typename T, class... U>void in(T &t, U &...u) {cin >> t;in(u...);}void out() { cout << "\n"; }template <typename T, class... U, char sep = ' '>void out(const T &t, const U &...u) {cout << t;if (sizeof...(u)) cout << sep;out(u...);}struct IoSetupNya {IoSetupNya() {cin.tie(nullptr);ios::sync_with_stdio(false);cout << fixed << setprecision(15);cerr << fixed << setprecision(7);}} iosetupnya;} // namespace Nyaan// debug#ifdef NyaanDebug#define trc(...) (void(0))#endif#ifndef NyaanDebug#define trc(...) (void(0))#endif#ifndef NyaanLocal#define trc2(...) (void(0))#endif// macro#define each(x, v) for (auto&& x : v)#define each2(x, y, v) for (auto&& [x, y] : v)#define all(v) (v).begin(), (v).end()#define rep(i, N) for (long long i = 0; i < (long long)(N); i++)#define repr(i, N) for (long long i = (long long)(N)-1; i >= 0; i--)#define rep1(i, N) for (long long i = 1; i <= (long long)(N); i++)#define repr1(i, N) for (long long i = (N); (long long)(i) > 0; i--)#define reg(i, a, b) for (long long i = (a); i < (b); i++)#define regr(i, a, b) for (long long i = (b)-1; i >= (a); i--)#define fi first#define se second#define ini(...) \int __VA_ARGS__; \in(__VA_ARGS__)#define inl(...) \long long __VA_ARGS__; \in(__VA_ARGS__)#define ins(...) \string __VA_ARGS__; \in(__VA_ARGS__)#define in2(s, t) \for (int i = 0; i < (int)s.size(); i++) { \in(s[i], t[i]); \}#define in3(s, t, u) \for (int i = 0; i < (int)s.size(); i++) { \in(s[i], t[i], u[i]); \}#define in4(s, t, u, v) \for (int i = 0; i < (int)s.size(); i++) { \in(s[i], t[i], u[i], v[i]); \}#define die(...) \do { \Nyaan::out(__VA_ARGS__); \return; \} while (0)namespace Nyaan {void solve();}int main() { Nyaan::solve(); }//template <typename T>struct edge {int src, to;T cost;edge(int _to, T _cost) : src(-1), to(_to), cost(_cost) {}edge(int _src, int _to, T _cost) : src(_src), to(_to), cost(_cost) {}edge &operator=(const int &x) {to = x;return *this;}operator int() const { return to; }};template <typename T>using Edges = vector<edge<T>>;template <typename T>using WeightedGraph = vector<Edges<T>>;using UnweightedGraph = vector<vector<int>>;// Input of (Unweighted) GraphUnweightedGraph graph(int N, int M = -1, bool is_directed = false,bool is_1origin = true) {UnweightedGraph g(N);if (M == -1) M = N - 1;for (int _ = 0; _ < M; _++) {int x, y;cin >> x >> y;if (is_1origin) x--, y--;g[x].push_back(y);if (!is_directed) g[y].push_back(x);}return g;}// Input of Weighted Graphtemplate <typename T>WeightedGraph<T> wgraph(int N, int M = -1, bool is_directed = false,bool is_1origin = true) {WeightedGraph<T> g(N);if (M == -1) M = N - 1;for (int _ = 0; _ < M; _++) {int x, y;cin >> x >> y;T c;cin >> c;if (is_1origin) x--, y--;g[x].emplace_back(x, y, c);if (!is_directed) g[y].emplace_back(y, x, c);}return g;}// Input of Edgestemplate <typename T>Edges<T> esgraph([[maybe_unused]] int N, int M, int is_weighted = true,bool is_1origin = true) {Edges<T> es;for (int _ = 0; _ < M; _++) {int x, y;cin >> x >> y;T c;if (is_weighted)cin >> c;elsec = 1;if (is_1origin) x--, y--;es.emplace_back(x, y, c);}return es;}// Input of Adjacency Matrixtemplate <typename T>vector<vector<T>> adjgraph(int N, int M, T INF, int is_weighted = true,bool is_directed = false, bool is_1origin = true) {vector<vector<T>> d(N, vector<T>(N, INF));for (int _ = 0; _ < M; _++) {int x, y;cin >> x >> y;T c;if (is_weighted)cin >> c;elsec = 1;if (is_1origin) x--, y--;d[x][y] = c;if (!is_directed) d[y][x] = c;}return d;}/*** @brief グラフテンプレート* @docs docs/graph/graph-template.md*///template <uint32_t mod>struct LazyMontgomeryModInt {using mint = LazyMontgomeryModInt;using i32 = int32_t;using u32 = uint32_t;using u64 = uint64_t;static constexpr u32 get_r() {u32 ret = mod;for (i32 i = 0; i < 4; ++i) ret *= 2 - mod * ret;return ret;}static constexpr u32 r = get_r();static constexpr u32 n2 = -u64(mod) % mod;static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30");static_assert((mod & 1) == 1, "invalid, mod % 2 == 0");static_assert(r * mod == 1, "this code has bugs.");u32 a;constexpr LazyMontgomeryModInt() : a(0) {}constexpr LazyMontgomeryModInt(const int64_t &b): a(reduce(u64(b % mod + mod) * n2)){};static constexpr u32 reduce(const u64 &b) {return (b + u64(u32(b) * u32(-r)) * mod) >> 32;}constexpr mint &operator+=(const mint &b) {if (i32(a += b.a - 2 * mod) < 0) a += 2 * mod;return *this;}constexpr mint &operator-=(const mint &b) {if (i32(a -= b.a) < 0) a += 2 * mod;return *this;}constexpr mint &operator*=(const mint &b) {a = reduce(u64(a) * b.a);return *this;}constexpr mint &operator/=(const mint &b) {*this *= b.inverse();return *this;}constexpr mint operator+(const mint &b) const { return mint(*this) += b; }constexpr mint operator-(const mint &b) const { return mint(*this) -= b; }constexpr mint operator*(const mint &b) const { return mint(*this) *= b; }constexpr mint operator/(const mint &b) const { return mint(*this) /= b; }constexpr bool operator==(const mint &b) const {return (a >= mod ? a - mod : a) == (b.a >= mod ? b.a - mod : b.a);}constexpr bool operator!=(const mint &b) const {return (a >= mod ? a - mod : a) != (b.a >= mod ? b.a - mod : b.a);}constexpr mint operator-() const { return mint() - mint(*this); }constexpr mint operator+() const { return mint(*this); }constexpr mint pow(u64 n) const {mint ret(1), mul(*this);while (n > 0) {if (n & 1) ret *= mul;mul *= mul;n >>= 1;}return ret;}constexpr mint inverse() const {int x = get(), y = mod, u = 1, v = 0, t = 0, tmp = 0;while (y > 0) {t = x / y;x -= t * y, u -= t * v;tmp = x, x = y, y = tmp;tmp = u, u = v, v = tmp;}return mint{u};}friend ostream &operator<<(ostream &os, const mint &b) {return os << b.get();}friend istream &operator>>(istream &is, mint &b) {int64_t t;is >> t;b = LazyMontgomeryModInt<mod>(t);return (is);}constexpr u32 get() const {u32 ret = reduce(a);return ret >= mod ? ret - mod : ret;}static constexpr u32 get_mod() { return mod; }};using namespace std;// コンストラクタの MAX に 「C(n, r) や fac(n) でクエリを投げる最大の n 」// を入れると倍速くらいになる// mod を超えて前計算して 0 割りを踏むバグは対策済みtemplate <typename T>struct Binomial {vector<T> f, g, h;Binomial(int MAX = 0) {assert(T::get_mod() != 0 && "Binomial<mint>()");f.resize(1, T{1});g.resize(1, T{1});h.resize(1, T{1});if (MAX > 0) extend(MAX + 1);}void extend(int m = -1) {int n = f.size();if (m == -1) m = n * 2;m = min<int>(m, T::get_mod());if (n >= m) return;f.resize(m);g.resize(m);h.resize(m);for (int i = n; i < m; i++) f[i] = f[i - 1] * T(i);g[m - 1] = f[m - 1].inverse();h[m - 1] = g[m - 1] * f[m - 2];for (int i = m - 2; i >= n; i--) {g[i] = g[i + 1] * T(i + 1);h[i] = g[i] * f[i - 1];}}T fac(int i) {if (i < 0) return T(0);while (i >= (int)f.size()) extend();return f[i];}T finv(int i) {if (i < 0) return T(0);while (i >= (int)g.size()) extend();return g[i];}T inv(int i) {if (i < 0) return -inv(-i);while (i >= (int)h.size()) extend();return h[i];}T C(int n, int r) {if (n < 0 || n < r || r < 0) return T(0);return fac(n) * finv(n - r) * finv(r);}inline T operator()(int n, int r) { return C(n, r); }template <typename I>T multinomial(const vector<I>& r) {static_assert(is_integral<I>::value == true);int n = 0;for (auto& x : r) {if (x < 0) return T(0);n += x;}T res = fac(n);for (auto& x : r) res *= finv(x);return res;}template <typename I>T operator()(const vector<I>& r) {return multinomial(r);}T C_naive(int n, int r) {if (n < 0 || n < r || r < 0) return T(0);T ret = T(1);r = min(r, n - r);for (int i = 1; i <= r; ++i) ret *= inv(i) * (n--);return ret;}T P(int n, int r) {if (n < 0 || n < r || r < 0) return T(0);return fac(n) * finv(n - r);}// [x^r] 1 / (1-x)^nT H(long long n, long long r) {if (n < 0 || r < 0) return T(0);return r == 0 ? 1 : C(n + r - 1, r);}};//using namespace std;using namespace std;using namespace std;namespace internal {unsigned long long non_deterministic_seed() {unsigned long long m =chrono::duration_cast<chrono::nanoseconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();m ^= 9845834732710364265uLL;m ^= m << 24, m ^= m >> 31, m ^= m << 35;return m;}unsigned long long deterministic_seed() { return 88172645463325252UL; }// 64 bit の seed 値を生成 (手元では seed 固定)// 連続で呼び出すと同じ値が何度も返ってくるので注意// #define RANDOMIZED_SEED するとシードがランダムになるunsigned long long seed() {#if defined(DETERMINISTIC_SEED)return deterministic_seed();#elif defined(NyaanLocal) && !defined(RANDOMIZED_SEED)return deterministic_seed();#elsereturn non_deterministic_seed();#endif}} // namespace internalusing namespace std;namespace internal {template <typename T>using is_broadly_integral =typename conditional_t<is_integral_v<T> || is_same_v<T, __int128_t> ||is_same_v<T, __uint128_t>,true_type, false_type>::type;template <typename T>using is_broadly_signed =typename conditional_t<is_signed_v<T> || is_same_v<T, __int128_t>,true_type, false_type>::type;template <typename T>using is_broadly_unsigned =typename conditional_t<is_unsigned_v<T> || is_same_v<T, __uint128_t>,true_type, false_type>::type;#define ENABLE_VALUE(x) \template <typename T> \constexpr bool x##_v = x<T>::value;ENABLE_VALUE(is_broadly_integral);ENABLE_VALUE(is_broadly_signed);ENABLE_VALUE(is_broadly_unsigned);#undef ENABLE_VALUE#define ENABLE_HAS_TYPE(var) \template <class, class = void> \struct has_##var : false_type {}; \template <class T> \struct has_##var<T, void_t<typename T::var>> : true_type {}; \template <class T> \constexpr auto has_##var##_v = has_##var<T>::value;#define ENABLE_HAS_VAR(var) \template <class, class = void> \struct has_##var : false_type {}; \template <class T> \struct has_##var<T, void_t<decltype(T::var)>> : true_type {}; \template <class T> \constexpr auto has_##var##_v = has_##var<T>::value;} // namespace internalnamespace internal {// 整数, 整数列を 64 bit unsigned int へ移すusing u64 = unsigned long long;using u128 = __uint128_t;ENABLE_HAS_TYPE(first_type);ENABLE_HAS_TYPE(second_type);ENABLE_HAS_TYPE(iterator);template <typename T>u64 hash_function(const T& x) {static u64 r = seed();constexpr u64 z1 = 11995408973635179863ULL;if constexpr (is_broadly_integral_v<T>) {// Integralreturn (u64(x) ^ r) * z1;} else if constexpr (has_first_type_v<T> && has_second_type_v<T>) {// pairconstexpr u64 z2 = 10150724397891781847ULL;return hash_function(x.first) + hash_function(x.second) * z2;} else if constexpr (has_iterator_v<T>) {// Containerconstexpr u64 mod = (1LL << 61) - 1;constexpr u64 base = 950699498548472943ULL;u64 m = 0;for (auto& z : x) {u64 w;if constexpr (is_broadly_integral_v<T>) {w = u64(z) ^ r;} else {w = hash_function(z);}u128 y = u128(m) * base + (w & mod);m = (y & mod) + (y >> 61);if (m >= mod) m -= mod;}m ^= m << 24, m ^= m >> 31, m ^= m << 35;return m;} else {static_assert([]() { return false; }());}}template <typename Key>struct HashObject {size_t operator()(const Key& x) const { return hash_function(x); }};} // namespace internal/*@brief ハッシュ関数*/// 削除不可能な hashmap//// テンプレート引数// fixed_size : これを true にするするとバケットサイズが固定になる// get_hash : ハッシュ関数の指定// 引数// _default_value : val の初期値, この値で初期化// _default_size :// バケットサイズ, max(4, _default_size) 以上の 2 べきで初期化// ただし fixed_size が true の時にしかサイズを変更できないtemplate <typename Key, typename Val, bool fixed_size = false,unsigned long long (*get_hash)(const Key&) =internal::hash_function<Key>>struct UnerasableHashMap {int N, occupied_num, shift;vector<Key> keys;vector<Val> vals;vector<char> flag;Val default_value;int default_size;// サイズを n に変更するvoid init(int n, bool reset = false) {assert(n >= 4 && (n & (n - 1)) == 0);if constexpr (fixed_size) {assert(reset == true);n = N;}if (reset == true) {N = n, occupied_num = 0, shift = 64 - __builtin_ctz(n);keys.resize(n);vals.resize(n);flag.resize(n);fill(begin(vals), end(vals), default_value);fill(begin(flag), end(flag), 0);} else {N = n, shift = 64 - __builtin_ctz(n);vector<Key> keys2(n);vector<Val> vals2(n, default_value);vector<char> flag2(n);swap(keys, keys2), swap(vals, vals2), swap(flag, flag2);for (int i = 0; i < (int)flag2.size(); i++) {if (flag2[i]) {int j = hint(keys2[i]);keys[j] = keys2[i], vals[j] = vals2[i], flag[j] = 1;}}}}UnerasableHashMap(const Val& _default_value = Val{}, int _default_size = 4): occupied_num(0), default_value(_default_value) {if (fixed_size == false) _default_size = 4;N = 4;while (N < _default_size) N *= 2;default_size = N;init(N, true);}int hint(const Key& k) {int hash = get_hash(k) >> shift;while (flag[hash] && keys[hash] != k) hash = (hash + 1) & (N - 1);return hash;}// key が i である要素への参照を返すVal& operator[](const Key& k) {int i = hint(k);if (!flag[i]) {if constexpr (fixed_size == false) {if (occupied_num * 2 >= N) {init(2 * N), i = hint(k);}}keys[i] = k, flag[i] = 1, occupied_num++;}return vals[i];}Val get(const Key& k) {int i = hint(k);return flag[i] ? vals[i] : default_value;}// 走査, f に関数 f(key, val) を入れるtemplate <typename F>void enumerate(const F f) {for (int i = 0; i < (int)flag.size(); i++) {if (flag[i]) f(keys[i], vals[i]);}}int count(const Key& k) { return flag[hint(k)]; }bool contain(const Key& k) { return flag[hint(k)]; }int size() const { return occupied_num; }void reset() { init(default_size, true); }void clear() { init(default_size, true); }};namespace nachia {template <class Elem>class CsrArray {public:struct ListRange {using iterator = typename std::vector<Elem>::iterator;iterator begi, endi;iterator begin() const { return begi; }iterator end() const { return endi; }int size() const { return (int)std::distance(begi, endi); }Elem& operator[](int i) const { return begi[i]; }};struct ConstListRange {using iterator = typename std::vector<Elem>::const_iterator;iterator begi, endi;iterator begin() const { return begi; }iterator end() const { return endi; }int size() const { return (int)std::distance(begi, endi); }const Elem& operator[](int i) const { return begi[i]; }};private:int m_n;std::vector<Elem> m_list;std::vector<int> m_pos;public:CsrArray() : m_n(0), m_list(), m_pos() {}static CsrArray Construct(int n, std::vector<std::pair<int, Elem>> items) {CsrArray res;res.m_n = n;std::vector<int> buf(n + 1, 0);for (auto& [u, v] : items) {++buf[u];}for (int i = 1; i <= n; i++) buf[i] += buf[i - 1];res.m_list.resize(buf[n]);for (int i = (int)items.size() - 1; i >= 0; i--) {res.m_list[--buf[items[i].first]] = std::move(items[i].second);}res.m_pos = std::move(buf);return res;}static CsrArray FromRaw(std::vector<Elem> list, std::vector<int> pos) {CsrArray res;res.m_n = pos.size() - 1;res.m_list = std::move(list);res.m_pos = std::move(pos);return res;}ListRange operator[](int u) {return ListRange{m_list.begin() + m_pos[u], m_list.begin() + m_pos[u + 1]};}ConstListRange operator[](int u) const {return ConstListRange{m_list.begin() + m_pos[u],m_list.begin() + m_pos[u + 1]};}int size() const { return m_n; }int fullSize() const { return (int)m_list.size(); }};} // namespace nachianamespace nachia {struct Graph {public:struct Edge {int from, to;void reverse() { std::swap(from, to); }int xorval() const { return from ^ to; }};Graph(int n = 0, bool undirected = false, int m = 0): m_n(n), m_e(m), m_isUndir(undirected) {}Graph(int n, const std::vector<std::pair<int, int>>& edges,bool undirected = false): m_n(n), m_isUndir(undirected) {m_e.resize(edges.size());for (std::size_t i = 0; i < edges.size(); i++)m_e[i] = {edges[i].first, edges[i].second};}template <class Cin>static Graph Input(Cin& cin, int n, bool undirected, int m, bool offset = 0) {Graph res(n, undirected, m);for (int i = 0; i < m; i++) {int u, v;cin >> u >> v;res[i].from = u - offset;res[i].to = v - offset;}return res;}int numVertices() const noexcept { return m_n; }int numEdges() const noexcept { return int(m_e.size()); }int addNode() noexcept { return m_n++; }int addEdge(int from, int to) {m_e.push_back({from, to});return numEdges() - 1;}Edge& operator[](int ei) noexcept { return m_e[ei]; }const Edge& operator[](int ei) const noexcept { return m_e[ei]; }Edge& at(int ei) { return m_e.at(ei); }const Edge& at(int ei) const { return m_e.at(ei); }auto begin() { return m_e.begin(); }auto end() { return m_e.end(); }auto begin() const { return m_e.begin(); }auto end() const { return m_e.end(); }bool isUndirected() const noexcept { return m_isUndir; }void reverseEdges() noexcept {for (auto& e : m_e) e.reverse();}void contract(int newV, const std::vector<int>& mapping) {assert(numVertices() == int(mapping.size()));for (int i = 0; i < numVertices(); i++)assert(0 <= mapping[i] && mapping[i] < newV);for (auto& e : m_e) {e.from = mapping[e.from];e.to = mapping[e.to];}m_n = newV;}std::vector<Graph> induce(int num, const std::vector<int>& mapping) const {int n = numVertices();assert(n == int(mapping.size()));for (int i = 0; i < n; i++) assert(-1 <= mapping[i] && mapping[i] < num);std::vector<int> indexV(n), newV(num);for (int i = 0; i < n; i++)if (mapping[i] >= 0) indexV[i] = newV[mapping[i]]++;std::vector<Graph> res;res.reserve(num);for (int i = 0; i < num; i++) res.emplace_back(newV[i], isUndirected());for (auto e : m_e)if (mapping[e.from] == mapping[e.to] && mapping[e.to] >= 0)res[mapping[e.to]].addEdge(indexV[e.from], indexV[e.to]);return res;}CsrArray<int> getEdgeIndexArray(bool undirected) const {std::vector<std::pair<int, int>> src;src.reserve(numEdges() * (undirected ? 2 : 1));for (int i = 0; i < numEdges(); i++) {auto e = operator[](i);src.emplace_back(e.from, i);if (undirected) src.emplace_back(e.to, i);}return CsrArray<int>::Construct(numVertices(), src);}CsrArray<int> getEdgeIndexArray() const {return getEdgeIndexArray(isUndirected());}CsrArray<int> getAdjacencyArray(bool undirected) const {std::vector<std::pair<int, int>> src;src.reserve(numEdges() * (undirected ? 2 : 1));for (auto e : m_e) {src.emplace_back(e.from, e.to);if (undirected) src.emplace_back(e.to, e.from);}return CsrArray<int>::Construct(numVertices(), src);}CsrArray<int> getAdjacencyArray() const {return getAdjacencyArray(isUndirected());}private:int m_n;std::vector<Edge> m_e;bool m_isUndir;};} // namespace nachianamespace nachia {// simple graph// for each edge// O( n + m sqrt(m) ) timetemplate <class Weight>std::vector<long long> CountC4Simple(int n, std::vector<int> U,std::vector<int> V,std::vector<Weight> W) {int m = int(W.size());// less incident edges, smaller indexstd::vector<int> deg(n);for (int e = 0; e < m; e++) {deg[U[e]]++;deg[V[e]]++;}std::vector<int> I(n);for (int i = 0; i < n; i++) I[i] = i;std::sort(I.begin(), I.end(), [&](int l, int r) { return deg[l] < deg[r]; });{std::vector<int> O(n);for (int i = 0; i < n; i++) O[I[i]] = i;for (int& u : U) u = O[u];for (int& u : V) u = O[u];}for (int e = 0; e < m; e++)if (U[e] < V[e]) std::swap(U[e], V[e]);// adjacency liststd::vector<int> estart(n);for (int i = 0; i < n - 1; i++) estart[i + 1] = estart[i] + deg[I[i]];std::vector<int> eend = estart;std::vector<int> eid(m * 2);std::vector<int> eto(m * 2);for (int e = 0; e < m; e++) {int v = U[e];int w = V[e];eid[eend[v]] = e;eto[eend[v]] = w;eend[v]++;}std::vector<int> eendx = eend;for (int v = 0; v < n; v++) {for (int i = estart[v]; i < eendx[v]; i++) {int e = eid[i];int w = eto[i];eid[eend[w]] = e;eto[eend[w]] = v;eend[w]++;}}std::vector<Weight> c(n); // c[x] : number of paths(v --> w --> x)std::vector<Weight> ans(m);for (int v = n - 1; v >= 0; v--) {for (int i = estart[v]; i < eend[v]; i++) {int evw = eid[i];int w = eto[i];eend[w] -= 1; // remove w -> vfor (int j = estart[w]; j < eend[w]; j++) {int ewx = eid[j];int x = eto[j];c[x] += W[evw] * W[ewx];}}for (int i = estart[v]; i < eend[v]; i++) {int evw = eid[i];int w = eto[i];for (int j = estart[w]; j < eend[w]; j++) {int ewx = eid[j];int x = eto[j];Weight val = c[x] - W[evw] * W[ewx];ans[evw] += val * W[ewx];ans[ewx] += val * W[evw];}}for (int i = estart[v]; i < eend[v]; i++) {int w = eto[i];for (int j = estart[w]; j < eend[w]; j++) c[eto[j]] = 0;}}return ans;}// for each edge// O( n + m sqrt(m) ) timetemplate <class Weight>std::vector<Weight> CountC4(int n, std::vector<int> U, std::vector<int> V,std::vector<Weight> W) {int m = int(W.size());for (int i = 0; i < m; i++)if (U[i] > V[i]) std::swap(U[i], V[i]);std::vector<int> I(m);for (int i = 0; i < m; i++) I[i] = i;std::sort(I.begin(), I.end(), [&](int l, int r) { return V[l] < V[r]; });std::stable_sort(I.begin(), I.end(),[&](int l, int r) { return U[l] < U[r]; });std::vector<int> Q(m);std::vector<int> U2;std::vector<int> V2;std::vector<Weight> W2;for (int i = 0; i < m; i++) {int e = I[i];if (i == 0 || U2.back() != U[e] || V2.back() != V[e]) {U2.push_back(U[e]);V2.push_back(V[e]);W2.push_back(0);}W2.back() += W[e];Q[e] = int(U2.size()) - 1;}auto simple_res =CountC4Simple<Weight>(n, std::move(U2), std::move(V2), std::move(W2));std::vector<Weight> ans(m);for (int e = 0; e < m; e++) ans[e] = simple_res[Q[e]];return ans;}} // namespace nachia//using namespace Nyaan;using mint = LazyMontgomeryModInt<998244353>;// using mint = LazyMontgomeryModInt<1000000007>;using vm = vector<mint>;using vvm = vector<vm>;using namespace Nyaan;template <typename F>void enumerate_triangle(const vvi& g, const F& f) {int N = sz(g);auto ord = mkord(N, [&](int i, int j) { return g[i].size() < g[j].size(); });auto inv = mkinv(ord);vvi h(N);vp es;rep(i, N) each(j, g[i]) if (inv[i] < inv[j]) {es.emplace_back(i, j), h[i].push_back(j);}V<bool> flg(N, 0);each(e, es) {each(u, h[e.first]) flg[u] = 1;each(v, h[e.second]) if (flg[v]) f(v, e.first, e.second);each(u, h[e.first]) flg[u] = 0;}}void q() {ini(N, M);vvi g = graph(N, M);vp es;rep(i, N) each(j, g[i]) if (i < j) es.emplace_back(i, j);vi deg(N);rep(i, N) deg[i] = sz(g[i]);ll C3_num = 0;enumerate_triangle(g, [&](int, int, int) { C3_num++; });ll C4_num = 0;{vi u, v;each2(i, j, es) u.push_back(i), v.push_back(j);auto c4 = nachia::CountC4(N, u, v, vl(M, 1));C4_num = Sum(c4) / 4;}Binomial<mint> C;auto f1 = [&]() -> mint { return C(N, 4); };auto f2 = [&]() -> mint { return C(N - 2, 2) * M; };auto f3 = [&]() -> mint {mint s = 0;rep(i, N) s += C(deg[i], 2);return s * (N - 3);};auto f4 = [&]() -> mint {mint s = 0;rep(i, N) s += C(deg[i], 2);return C(M, 2) - s;};auto f5 = [&]() -> mint {mint s = 0;rep(i, N) s += C(deg[i], 3);return s;};auto f6 = [&]() -> mint {mint s = 0;each2(u, v, es) s += (deg[u] - 1) * (deg[v] - 1);s -= 3 * C3_num;return s;};auto f7 = [&]() -> mint { return mint{C3_num} * (N - 3); };auto f8 = [&]() -> mint { return C4_num; };auto f9 = [&]() -> mint {vi cnt(N);enumerate_triangle(g, [&](int i, int j, int k) { cnt[i]++, cnt[j]++, cnt[k]++; });mint s = 0;rep(u, N) s += 1LL * (deg[u] - 2) * cnt[u];return s;};auto f10 = [&]() -> mint {UnerasableHashMap<ll, ll> mp;enumerate_triangle(g, [&](ll i, ll j, ll k) {mp[(min(i, j) << 32) + max(i, j)]++;mp[(min(j, k) << 32) + max(j, k)]++;mp[(min(k, i) << 32) + max(k, i)]++;});mint s = 0;mp.enumerate([&](ll, ll val) { s += C(val, 2); });return s;};trc(f1(), f2(), f3(), f4(), f5(), f6(), f7(), f8(), f9(), f10());mint T = mint{-1} / 3;mint ans = 0;ans += f1();ans -= f2();ans += f3();ans += f4();ans -= f5();ans -= f6();ans -= f7();ans += f8() * 2 / 3;ans += f9();ans -= f10() * 2 / 3;out(T, ans);// trc(mint{13} + T * 2);}void Nyaan::solve() {int t = 1;// in(t);while (t--) q();}