結果
問題 | No.2985 May Count Induced C4 Subgraphs |
ユーザー |
👑 |
提出日時 | 2024-12-10 20:17:20 |
言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 3,185 ms / 5,000 ms |
コード長 | 50,430 bytes |
コンパイル時間 | 4,530 ms |
コンパイル使用メモリ | 305,156 KB |
実行使用メモリ | 118,448 KB |
最終ジャッジ日時 | 2024-12-10 20:17:58 |
合計ジャッジ時間 | 33,847 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge3 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | AC * 22 |
ソースコード
// #pragma GCC target("avx2")// #pragma GCC optimize("O3")// #pragma GCC optimize("unroll-loops")// #define INTERACTIVE#include <bits/stdc++.h>using namespace std;namespace templates {// typeusing ll = long long;using ull = unsigned long long;using Pii = pair<int, int>;using Pil = pair<int, ll>;using Pli = pair<ll, int>;using Pll = pair<ll, ll>;template <class T>using pq = priority_queue<T>;template <class T>using qp = priority_queue<T, vector<T>, greater<T>>;// clang-format off#define vec(T, A, ...) vector<T> A(__VA_ARGS__);#define vvec(T, A, h, ...) vector<vector<T>> A(h, vector<T>(__VA_ARGS__));#define vvvec(T, A, h1, h2, ...) vector<vector<vector<T>>> A(h1, vector<vector<T>>(h2, vector<T>(__VA_ARGS__)));// clang-format on// for loop#define fori1(a) for (ll _ = 0; _ < (a); _++)#define fori2(i, a) for (ll i = 0; i < (a); i++)#define fori3(i, a, b) for (ll i = (a); i < (b); i++)#define fori4(i, a, b, c) for (ll i = (a); ((c) > 0 || i > (b)) && ((c) < 0 || i < (b)); i += (c))#define overload4(a, b, c, d, e, ...) e#define fori(...) overload4(__VA_ARGS__, fori4, fori3, fori2, fori1)(__VA_ARGS__)// declare and input// clang-format off#define INT(...) int __VA_ARGS__; inp(__VA_ARGS__);#define LL(...) ll __VA_ARGS__; inp(__VA_ARGS__);#define STRING(...) string __VA_ARGS__; inp(__VA_ARGS__);#define CHAR(...) char __VA_ARGS__; inp(__VA_ARGS__);#define DOUBLE(...) double __VA_ARGS__; STRING(str___); __VA_ARGS__ = stod(str___);#define VEC(T, A, n) vector<T> A(n); inp(A);#define VVEC(T, A, n, m) vector<vector<T>> A(n, vector<T>(m)); inp(A);// clang-format on// const valueconst ll MOD1 = 1000000007;const ll MOD9 = 998244353;const double PI = acos(-1);// other macro#if !defined(RIN__LOCAL) && !defined(INTERACTIVE)#define endl "\n"#endif#define spa ' '#define len(A) ll(A.size())#define all(A) begin(A), end(A)// functionvector<char> stoc(string &S) {int n = S.size();vector<char> ret(n);for (int i = 0; i < n; i++) ret[i] = S[i];return ret;}string ctos(vector<char> &S) {int n = S.size();string ret = "";for (int i = 0; i < n; i++) ret += S[i];return ret;}template <class T>auto min(const T &a) {return *min_element(all(a));}template <class T>auto max(const T &a) {return *max_element(all(a));}template <class T, class S>auto clamp(T &a, const S &l, const S &r) {return (a > r ? r : a < l ? l : a);}template <class T, class S>inline bool chmax(T &a, const S &b) {return (a < b ? a = b, 1 : 0);}template <class T, class S>inline bool chmin(T &a, const S &b) {return (a > b ? a = b, 1 : 0);}template <class T, class S>inline bool chclamp(T &a, const S &l, const S &r) {auto b = clamp(a, l, r);return (a != b ? a = b, 1 : 0);}template <typename T>T sum(vector<T> &A) {T tot = 0;for (auto a : A) tot += a;return tot;}template <typename T>vector<T> compression(vector<T> X) {sort(all(X));X.erase(unique(all(X)), X.end());return X;}// input and outputnamespace io {// __int128_tstd::ostream &operator<<(std::ostream &dest, __int128_t value) {std::ostream::sentry s(dest);if (s) {__uint128_t tmp = value < 0 ? -value : value;char buffer[128];char *d = std::end(buffer);do {--d;*d = "0123456789"[tmp % 10];tmp /= 10;} while (tmp != 0);if (value < 0) {--d;*d = '-';}int len = std::end(buffer) - d;if (dest.rdbuf()->sputn(d, len) != len) {dest.setstate(std::ios_base::badbit);}}return dest;}// vector<T>template <typename T>istream &operator>>(istream &is, vector<T> &A) {for (auto &a : A) is >> a;return is;}template <typename T>ostream &operator<<(ostream &os, vector<T> &A) {for (size_t i = 0; i < A.size(); i++) {os << A[i];if (i != A.size() - 1) os << ' ';}return os;}// vector<vector<T>>template <typename T>istream &operator>>(istream &is, vector<vector<T>> &A) {for (auto &a : A) is >> a;return is;}template <typename T>ostream &operator<<(ostream &os, vector<vector<T>> &A) {for (size_t i = 0; i < A.size(); i++) {os << A[i];if (i != A.size() - 1) os << endl;}return os;}// pair<S, T>template <typename S, typename T>istream &operator>>(istream &is, pair<S, T> &A) {is >> A.first >> A.second;return is;}template <typename S, typename T>ostream &operator<<(ostream &os, pair<S, T> &A) {os << A.first << ' ' << A.second;return os;}// vector<pair<S, T>>template <typename S, typename T>istream &operator>>(istream &is, vector<pair<S, T>> &A) {for (size_t i = 0; i < A.size(); i++) {is >> A[i];}return is;}template <typename S, typename T>ostream &operator<<(ostream &os, vector<pair<S, T>> &A) {for (size_t i = 0; i < A.size(); i++) {os << A[i];if (i != A.size() - 1) os << endl;}return os;}// tupletemplate <typename T, size_t N>struct TuplePrint {static ostream &print(ostream &os, const T &t) {TuplePrint<T, N - 1>::print(os, t);os << ' ' << get<N - 1>(t);return os;}};template <typename T>struct TuplePrint<T, 1> {static ostream &print(ostream &os, const T &t) {os << get<0>(t);return os;}};template <typename... Args>ostream &operator<<(ostream &os, const tuple<Args...> &t) {TuplePrint<decltype(t), sizeof...(Args)>::print(os, t);return os;}// io functionsvoid FLUSH() {cout << flush;}void print() {cout << endl;}template <class Head, class... Tail>void print(Head &&head, Tail &&...tail) {cout << head;if (sizeof...(Tail)) cout << spa;print(std::forward<Tail>(tail)...);}template <typename T, typename S>void prisep(vector<T> &A, S sep) {int n = A.size();for (int i = 0; i < n; i++) {cout << A[i];if (i != n - 1) cout << sep;}cout << endl;}template <typename T, typename S>void priend(T A, S end) {cout << A << end;}template <typename T>void prispa(T A) {priend(A, spa);}template <typename T, typename S>bool printif(bool f, T A, S B) {if (f)print(A);elseprint(B);return f;}template <class... T>void inp(T &...a) {(cin >> ... >> a);}} // namespace iousing namespace io;// read graphvector<vector<int>> read_edges(int n, int m, bool direct = false, int indexed = 1) {vector<vector<int>> edges(n, vector<int>());for (int i = 0; i < m; i++) {INT(u, v);u -= indexed;v -= indexed;edges[u].push_back(v);if (!direct) edges[v].push_back(u);}return edges;}vector<vector<int>> read_tree(int n, int indexed = 1) {return read_edges(n, n - 1, false, indexed);}template <typename T = long long>vector<vector<pair<int, T>>> read_wedges(int n, int m, bool direct = false, int indexed = 1) {vector<vector<pair<int, T>>> edges(n, vector<pair<int, T>>());for (int i = 0; i < m; i++) {INT(u, v);T w;inp(w);u -= indexed;v -= indexed;edges[u].push_back({v, w});if (!direct) edges[v].push_back({u, w});}return edges;}template <typename T = long long>vector<vector<pair<int, T>>> read_wtree(int n, int indexed = 1) {return read_wedges<T>(n, n - 1, false, indexed);}// yes / nonamespace yesno {// yesinline bool yes(bool f = true) {cout << (f ? "yes" : "no") << endl;return f;}inline bool Yes(bool f = true) {cout << (f ? "Yes" : "No") << endl;return f;}inline bool YES(bool f = true) {cout << (f ? "YES" : "NO") << endl;return f;}// noinline bool no(bool f = true) {cout << (!f ? "yes" : "no") << endl;return f;}inline bool No(bool f = true) {cout << (!f ? "Yes" : "No") << endl;return f;}inline bool NO(bool f = true) {cout << (!f ? "YES" : "NO") << endl;return f;}// possibleinline bool possible(bool f = true) {cout << (f ? "possible" : "impossible") << endl;return f;}inline bool Possible(bool f = true) {cout << (f ? "Possible" : "Impossible") << endl;return f;}inline bool POSSIBLE(bool f = true) {cout << (f ? "POSSIBLE" : "IMPOSSIBLE") << endl;return f;}// impossibleinline bool impossible(bool f = true) {cout << (!f ? "possible" : "impossible") << endl;return f;}inline bool Impossible(bool f = true) {cout << (!f ? "Possible" : "Impossible") << endl;return f;}inline bool IMPOSSIBLE(bool f = true) {cout << (!f ? "POSSIBLE" : "IMPOSSIBLE") << endl;return f;}// Alice Bobinline bool Alice(bool f = true) {cout << (f ? "Alice" : "Bob") << endl;return f;}inline bool Bob(bool f = true) {cout << (f ? "Bob" : "Alice") << endl;return f;}// Takahashi Aokiinline bool Takahashi(bool f = true) {cout << (f ? "Takahashi" : "Aoki") << endl;return f;}inline bool Aoki(bool f = true) {cout << (f ? "Aoki" : "Takahashi") << endl;return f;}} // namespace yesnousing namespace yesno;} // namespace templatesusing namespace templates;/// https://nachiavivias.github.io/cp-library/cpp/graph/count-c4.htmlnamespace 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, int 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<Weight> CountC4Simple(int n, Graph g, std::vector<Weight> W) {int m = int(W.size());// less incident edges, smaller indexstd::vector<int> deg(n);for (auto [u, v] : g) {deg[u]++;deg[v]++;}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 (auto &[u, v] : g) {u = O[u], v = O[v];}}for (auto &e : g)if (e.from < e.to) e.reverse();// 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++) {auto [v, w] = g[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, Graph g, std::vector<Weight> W) {int m = int(W.size());for (auto &e : g)if (e.to < e.from) e.reverse();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 g[l].from != g[r].from ? g[l].from < g[r].from : g[l].to < g[r].to;});std::vector<int> Q(m);Graph g2;int g2sz = 0;std::vector<Weight> W2;for (auto e : I) {if (g2sz == 0 || g2[g2sz - 1].from != g[e].from || g2[g2sz - 1].to != g[e].to) {g2.addEdge(g[e].from, g[e].to);W2.push_back(0);g2sz++;}W2.back() += W[e];Q[e] = g2sz - 1;}auto simple_res = CountC4Simple<Weight>(n, std::move(g2), std::move(W2));std::vector<Weight> ans(m);for (int e = 0; e < m; e++) ans[e] = simple_res[Q[e]];return ans;}} // namespace nachiatemplate <int MOD>struct Modint {int x;Modint() : x(0) {}Modint(int64_t y) {if (y >= 0)x = y % MOD;elsex = (y % MOD + MOD) % MOD;}Modint &operator+=(const Modint &p) {x += p.x;if (x >= MOD) x -= MOD;return *this;}Modint &operator-=(const Modint &p) {x -= p.x;if (x < 0) x += MOD;return *this;}Modint &operator*=(const Modint &p) {x = int(1LL * x * p.x % MOD);return *this;}Modint &operator/=(const Modint &p) {*this *= p.inverse();return *this;}Modint &operator%=(const Modint &p) {assert(p.x == 0);return *this;}Modint operator-() const {return Modint(-x);}Modint &operator++() {x++;if (x == MOD) x = 0;return *this;}Modint &operator--() {if (x == 0) x = MOD;x--;return *this;}Modint operator++(int) {Modint result = *this;++*this;return result;}Modint operator--(int) {Modint result = *this;--*this;return result;}friend Modint operator+(const Modint &lhs, const Modint &rhs) {return Modint(lhs) += rhs;}friend Modint operator-(const Modint &lhs, const Modint &rhs) {return Modint(lhs) -= rhs;}friend Modint operator*(const Modint &lhs, const Modint &rhs) {return Modint(lhs) *= rhs;}friend Modint operator/(const Modint &lhs, const Modint &rhs) {return Modint(lhs) /= rhs;}friend Modint operator%(const Modint &lhs, const Modint &rhs) {assert(rhs.x == 0);return Modint(lhs);}bool operator==(const Modint &p) const {return x == p.x;}bool operator!=(const Modint &p) const {return x != p.x;}bool operator<(const Modint &rhs) const {return x < rhs.x;}bool operator<=(const Modint &rhs) const {return x <= rhs.x;}bool operator>(const Modint &rhs) const {return x > rhs.x;}bool operator>=(const Modint &rhs) const {return x >= rhs.x;}Modint inverse() const {int a = x, b = MOD, u = 1, v = 0, t;while (b > 0) {t = a / b;a -= t * b;u -= t * v;std::swap(a, b);std::swap(u, v);}return Modint(u);}Modint pow(int64_t k) const {Modint ret(1);Modint y(x);while (k > 0) {if (k & 1) ret *= y;y *= y;k >>= 1;}return ret;}std::pair<int, int> to_frac(int max_n = 1000) const {int y = x;for (int i = 1; i <= max_n; i++) {if (y <= max_n) {return {y, i};} else if (MOD - y <= max_n) {return {-(MOD - y), i};}y = (y + x) % MOD;}return {-1, -1};}friend std::ostream &operator<<(std::ostream &os, const Modint &p) {return os << p.x;}friend std::istream &operator>>(std::istream &is, Modint &p) {int64_t y;is >> y;p = Modint<MOD>(y);return (is);}static int get_mod() {return MOD;}};struct Arbitrary_Modint {int x;static int MOD;static void set_mod(int mod) {MOD = mod;}Arbitrary_Modint() : x(0) {}Arbitrary_Modint(int64_t y) {if (y >= 0)x = y % MOD;elsex = (y % MOD + MOD) % MOD;}Arbitrary_Modint &operator+=(const Arbitrary_Modint &p) {x += p.x;if (x >= MOD) x -= MOD;return *this;}Arbitrary_Modint &operator-=(const Arbitrary_Modint &p) {x -= p.x;if (x < 0) x += MOD;return *this;}Arbitrary_Modint &operator*=(const Arbitrary_Modint &p) {x = int(1LL * x * p.x % MOD);return *this;}Arbitrary_Modint &operator/=(const Arbitrary_Modint &p) {*this *= p.inverse();return *this;}Arbitrary_Modint &operator%=(const Arbitrary_Modint &p) {assert(p.x == 0);return *this;}Arbitrary_Modint operator-() const {return Arbitrary_Modint(-x);}Arbitrary_Modint &operator++() {x++;if (x == MOD) x = 0;return *this;}Arbitrary_Modint &operator--() {if (x == 0) x = MOD;x--;return *this;}Arbitrary_Modint operator++(int) {Arbitrary_Modint result = *this;++*this;return result;}Arbitrary_Modint operator--(int) {Arbitrary_Modint result = *this;--*this;return result;}friend Arbitrary_Modint operator+(const Arbitrary_Modint &lhs, const Arbitrary_Modint &rhs) {return Arbitrary_Modint(lhs) += rhs;}friend Arbitrary_Modint operator-(const Arbitrary_Modint &lhs, const Arbitrary_Modint &rhs) {return Arbitrary_Modint(lhs) -= rhs;}friend Arbitrary_Modint operator*(const Arbitrary_Modint &lhs, const Arbitrary_Modint &rhs) {return Arbitrary_Modint(lhs) *= rhs;}friend Arbitrary_Modint operator/(const Arbitrary_Modint &lhs, const Arbitrary_Modint &rhs) {return Arbitrary_Modint(lhs) /= rhs;}friend Arbitrary_Modint operator%(const Arbitrary_Modint &lhs, const Arbitrary_Modint &rhs) {assert(rhs.x == 0);return Arbitrary_Modint(lhs);}bool operator==(const Arbitrary_Modint &p) const {return x == p.x;}bool operator!=(const Arbitrary_Modint &p) const {return x != p.x;}bool operator<(const Arbitrary_Modint &rhs) {return x < rhs.x;}bool operator<=(const Arbitrary_Modint &rhs) {return x <= rhs.x;}bool operator>(const Arbitrary_Modint &rhs) {return x > rhs.x;}bool operator>=(const Arbitrary_Modint &rhs) {return x >= rhs.x;}Arbitrary_Modint inverse() const {int a = x, b = MOD, u = 1, v = 0, t;while (b > 0) {t = a / b;a -= t * b;u -= t * v;std::swap(a, b);std::swap(u, v);}return Arbitrary_Modint(u);}Arbitrary_Modint pow(int64_t k) const {Arbitrary_Modint ret(1);Arbitrary_Modint y(x);while (k > 0) {if (k & 1) ret *= y;y *= y;k >>= 1;}return ret;}friend std::ostream &operator<<(std::ostream &os, const Arbitrary_Modint &p) {return os << p.x;}friend std::istream &operator>>(std::istream &is, Arbitrary_Modint &p) {int64_t y;is >> y;p = Arbitrary_Modint(y);return (is);}static int get_mod() {return MOD;}};int Arbitrary_Modint::MOD = 998244353;using modint9 = Modint<998244353>;using modint1 = Modint<1000000007>;using modint = Arbitrary_Modint;using mint = modint9;template <typename T>struct Combination {int N;std::vector<T> fact, invfact;Combination(int N) : N(N) {fact.resize(N + 1);invfact.resize(N + 1);fact[0] = 1;for (int i = 1; i <= N; i++) {fact[i] = fact[i - 1] * i;}invfact[N] = T(1) / fact[N];for (int i = N - 1; i >= 0; i--) {invfact[i] = invfact[i + 1] * (i + 1);}}void extend(int n) {int le = fact.size();fact.resize(n + 1);invfact.resize(n + 1);for (int i = le; i <= n; i++) {fact[i] = fact[i - 1] * i;}invfact[n] = T(1) / fact[n];for (int i = n - 1; i >= le; i--) {invfact[i] = invfact[i + 1] * (i + 1);}}T nCk(int n, int k) {if (k > n || k < 0) return T(0);if (n >= int(fact.size())) extend(n);return fact[n] * invfact[k] * invfact[n - k];}T nPk(int n, int k) {if (k > n || k < 0) return T(0);if (n >= int(fact.size())) extend(n);return fact[n] * invfact[n - k];}T nHk(int n, int k) {if (n == 0 && k == 0) return T(1);return nCk(n + k - 1, k);}T catalan(int n) {return nCk(2 * n, n) - nCk(2 * n, n + 1);}// n 個の +1, m 個の -1, 累積和が常にk以下T catalan(int n, int m, int k) {if (n > m + k || k < 0)return T(0);elsereturn nCk(n + m, n) - nCk(n + m, m + k + 1);}// return [x^n] C^k(x)// 先頭に ( が k - 1 個連続するような長さ n + k - 1 の括弧列と一対一対応T catalan_convolution(int n, int k) {return catalan(k + n - 1, n, k - 1);}T narayana(int n, int k) {return nCk(n, k) * nCk(n, k - 1) / n;}T inv(int n) {assert(n >= 1);if (n >= int(fact.size())) extend(n);return invfact[n] * fact[n - 1];}};template <typename T, typename V>struct HashMap {std::vector<T> key;std::vector<V> value;std::vector<bool> used;uint32_t mask;std::vector<T> keys;HashMap(int n = 0) {int s = 4;while (s < n) s <<= 1;key.resize(s);value.resize(s);used.resize(s);keys.reserve(s);mask = s - 1;}size_t size() {return keys.size();}size_t hash(uint64_t x) {static const uint64_t FIXED_RANDOM =std::chrono::steady_clock::now().time_since_epoch().count();x += FIXED_RANDOM;x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL;x = x ^ (x >> 31);return x & mask;}int index(const int64_t &x) {size_t i = hash(x);while (used[i] && key[i] != x) {i++;if (i == key.size()) i = 0;}return i;}void extend() {std::vector<V> values;values.reserve(keys.size());for (auto k : keys) {values.push_back(get(k));}int n = key.size();key.resize(2 * n);value.resize(2 * n);used.assign(2 * n, false);keys.reserve(2 * n);mask = 2 * n - 1;for (size_t i = 0; i < keys.size(); i++) {auto k = index(keys[i]);used[k] = true;key[k] = keys[i];value[k] = values[i];}}V &operator[](const T &x) {if (keys.size() * 4 > key.size()) {extend();}int i = index(x);if (!used[i]) {used[i] = true;keys.push_back(x);}key[i] = x;return value[i];}V get(const T &x, const V &default_value = V()) {int i = index(x);return used[i] ? value[i] : default_value;}void clear() {keys.clear();used.assign(used.size(), false);}};void solve() {INT(n, m);Combination<mint> Comb(n + m + 10);vec(int, deg, n, 0);vec(Pii, E, m);using HM = HashMap<int, int>;vec(HM, E2, n);vvec(int, adj, n);fori(i, m) {INT(u, v);u--;v--;deg[u]++;deg[v]++;E[i] = {u, v};E2[u][v] = 1;E2[v][u] = 1;adj[u].push_back(v);adj[v].push_back(u);}/*0. 辺01. 辺12. 辺2 共有点あり3. 辺2 共有点なし4. 辺3 パス5. 辺3 サイクル6. 辺3 次数 3 あり7. 辺4 サイクル8. 辺4 C3 + 19. 辺5 -1辺10. 辺6 K4*/vector<vector<mint>> A({{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // nC4{4, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0}, // 次数 0{0, 2, 2, 4, 2, 0, 3, 0, 1, 0, 0}, // 次数 1{0, 0, 1, 0, 2, 3, 0, 4, 2, 2, 0}, // 次数 2{0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0}, // 特定の辺の両方に隣接なし * 2{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6}, // 特定の辺の両方に隣接 * 2{0, 0, 0, 0, 1, 0, 3, 4, 1, 0, 0}, // 特定の辺の必ず一方だけ隣接 * 2{0, 1, 0, 2, 0, 3, 0, 0, 1, 1, 6}, // 特定の辺の両方に隣接する or 両方に隣接しない * 2{0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 3}, // C4{0, 0, 0, 1, 1, 0, 0, 2, 1, 2, 3}, // クロス});int h = 10;int w = 11;if (false) {fori(i, h) {swap(A[i][0], A[i][w - 2]);swap(A[i][7], A[i][w - 1]);}fori(j, h) {int i1 = -1;fori(i, j, h) {if (A[i][j] != 0) {i1 = i;break;}}assert(i1 != -1);swap(A[j], A[i1]);mint inv = mint(1) / A[j][j];fori(k, w) {A[j][k] *= inv;}fori(i, h) {if (i == j) continue;mint x = A[i][j];fori(k, w) {A[i][k] -= A[j][k] * x;}}}print(A);return;}vec(mint, B, h);B[0] = Comb.nCk(n, 4);fori(i, n) {ll c = deg[i];ll d = n - 1 - c;B[1] += Comb.nCk(c, 0) * Comb.nCk(d, 3);B[2] += Comb.nCk(c, 1) * Comb.nCk(d, 2);B[3] += Comb.nCk(c, 2) * Comb.nCk(d, 1);}for (auto [u, v] : E) {if (deg[u] > deg[v]) {swap(u, v);}int a = 0;for (auto x : adj[u]) {if (E2[v].get(x) == 1) {a++;}}int b = (deg[u] - 1) + (deg[v] - 1) - 2 * a;int c = n - 2 - a - b;B[4] += Comb.nCk(c, 2);B[5] += Comb.nCk(a, 2);B[6] += Comb.nCk(b, 2);B[7] += Comb.nCk(a + c, 2);}{nachia::Graph g(n);for (auto [u, v] : E) {g.addEdge(u, v);}vec(mint, W, m, 1);auto res = nachia::CountC4(n, g, W);B[8] = sum(res) / 4;}for (auto [u, v] : E) {B[9] += m - deg[u] - deg[v] + 1;}B[9] /= 2;fori(i, h) {swap(A[i][0], A[i][w - 2]);swap(A[i][7], A[i][w - 1]);}fori(j, h) {int i1 = -1;fori(i, j, h) {if (A[i][j] != 0) {i1 = i;break;}}assert(i1 != -1);swap(A[j], A[i1]);swap(B[j], B[i1]);mint inv = mint(1) / A[j][j];fori(k, w) {A[j][k] *= inv;}B[j] *= inv;fori(i, h) {if (i == j) continue;mint x = A[i][j];fori(k, w) {A[i][k] -= A[j][k] * x;}B[i] -= B[j] * x;}}print(A[h - 1][w - 1], B[h - 1]);}int main() {#ifndef INTERACTIVEstd::cin.tie(0)->sync_with_stdio(0);#endif// std::cout << std::fixed << std::setprecision(12);int t;t = 1;// std::cin >> t;while (t--) solve();return 0;}// // #pragma GCC target("avx2")// // #pragma GCC optimize("O3")// // #pragma GCC optimize("unroll-loops")// // #define INTERACTIVE//// #include "kyopro-cpp/template.hpp"//// /// https://nachiavivias.github.io/cp-library/cpp/graph/count-c4.html//// 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 nachia//// namespace 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, int 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 nachia//// namespace nachia {//// // simple graph// // for each edge// // O( n + m sqrt(m) ) time// template <class Weight>// std::vector<Weight> CountC4Simple(int n, Graph g, std::vector<Weight> W) {// int m = int(W.size());//// // less incident edges, smaller index// std::vector<int> deg(n);// for (auto [u, v] : g) {// deg[u]++;// deg[v]++;// }// 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 (auto &[u, v] : g) {// u = O[u], v = O[v];// }// }//// for (auto &e : g)// if (e.from < e.to) e.reverse();//// // adjacency list//// std::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++) {// auto [v, w] = g[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 -> v// for (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) ) time// template <class Weight>// std::vector<Weight> CountC4(int n, Graph g, std::vector<Weight> W) {// int m = int(W.size());// for (auto &e : g)// if (e.to < e.from) e.reverse();// 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 g[l].from != g[r].from ? g[l].from < g[r].from : g[l].to < g[r].to;// });//// std::vector<int> Q(m);// Graph g2;// int g2sz = 0;// std::vector<Weight> W2;// for (auto e : I) {// if (g2sz == 0 || g2[g2sz - 1].from != g[e].from || g2[g2sz - 1].to != g[e].to) {// g2.addEdge(g[e].from, g[e].to);// W2.push_back(0);// g2sz++;// }// W2.back() += W[e];// Q[e] = g2sz - 1;// }//// auto simple_res = CountC4Simple<Weight>(n, std::move(g2), 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//// #include "misc/Modint.hpp"// using mint = modint9;// #include "math/Combination.hpp"//// #include "misc/HashMap.hpp"//// void solve() {// INT(n, m);// Combination<mint> Comb(n + m + 10);// vec(int, deg, n, 0);// vec(Pii, E, m);// using HM = HashMap<int, int>;// vec(HM, E2, n);// vvec(int, adj, n);// fori(i, m) {// INT(u, v);// u--;// v--;// deg[u]++;// deg[v]++;// E[i] = {u, v};// E2[u][v] = 1;// E2[v][u] = 1;// adj[u].push_back(v);// adj[v].push_back(u);// }//// /*// 0. 辺0// 1. 辺1// 2. 辺2 共有点あり// 3. 辺2 共有点なし// 4. 辺3 パス// 5. 辺3 サイクル// 6. 辺3 次数 3 あり// 7. 辺4 サイクル// 8. 辺4 C3 + 1// 9. 辺5 -1辺// 10. 辺6 K4// *///// vector<vector<mint>> A({// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // nC4// {4, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0}, // 次数 0// {0, 2, 2, 4, 2, 0, 3, 0, 1, 0, 0}, // 次数 1// {0, 0, 1, 0, 2, 3, 0, 4, 2, 2, 0}, // 次数 2// {0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0}, // 特定の辺の両方に隣接なし * 2// {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6}, // 特定の辺の両方に隣接 * 2// {0, 0, 0, 0, 1, 0, 3, 4, 1, 0, 0}, // 特定の辺の必ず一方だけ隣接 * 2// {0, 1, 0, 2, 0, 3, 0, 0, 1, 1, 6}, // 特定の辺の両方に隣接する or 両方に隣接しない * 2// {0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 3}, // C4// {0, 0, 0, 1, 1, 0, 0, 2, 1, 2, 3}, // クロス// });//// int h = 10;// int w = 11;// if (false) {// fori(i, h) {// swap(A[i][0], A[i][w - 2]);// swap(A[i][7], A[i][w - 1]);// }// fori(j, h) {// int i1 = -1;// fori(i, j, h) {// if (A[i][j] != 0) {// i1 = i;// break;// }// }// assert(i1 != -1);// swap(A[j], A[i1]);// mint inv = mint(1) / A[j][j];// fori(k, w) {// A[j][k] *= inv;// }//// fori(i, h) {// if (i == j) continue;// mint x = A[i][j];// fori(k, w) {// A[i][k] -= A[j][k] * x;// }// }// }// print(A);// return;// }//// vec(mint, B, h);// B[0] = Comb.nCk(n, 4);// fori(i, n) {// ll c = deg[i];// ll d = n - 1 - c;// B[1] += Comb.nCk(c, 0) * Comb.nCk(d, 3);// B[2] += Comb.nCk(c, 1) * Comb.nCk(d, 2);// B[3] += Comb.nCk(c, 2) * Comb.nCk(d, 1);// }//// for (auto [u, v] : E) {// if (deg[u] > deg[v]) {// swap(u, v);// }// int a = 0;// for (auto x : adj[u]) {// if (E2[v].get(x) == 1) {// a++;// }// }// int b = (deg[u] - 1) + (deg[v] - 1) - 2 * a;// int c = n - 2 - a - b;//// B[4] += Comb.nCk(c, 2);// B[5] += Comb.nCk(a, 2);// B[6] += Comb.nCk(b, 2);// B[7] += Comb.nCk(a + c, 2);// }//// {// nachia::Graph g(n);// for (auto [u, v] : E) {// g.addEdge(u, v);// }//// vec(mint, W, m, 1);// auto res = nachia::CountC4(n, g, W);// B[8] = sum(res) / 4;// }//// for (auto [u, v] : E) {// B[9] += m - deg[u] - deg[v] + 1;// }// B[9] /= 2;//// fori(i, h) {// swap(A[i][0], A[i][w - 2]);// swap(A[i][7], A[i][w - 1]);// }//// fori(j, h) {// int i1 = -1;// fori(i, j, h) {// if (A[i][j] != 0) {// i1 = i;// break;// }// }// assert(i1 != -1);// swap(A[j], A[i1]);// swap(B[j], B[i1]);// mint inv = mint(1) / A[j][j];// fori(k, w) {// A[j][k] *= inv;// }// B[j] *= inv;//// fori(i, h) {// if (i == j) continue;// mint x = A[i][j];// fori(k, w) {// A[i][k] -= A[j][k] * x;// }// B[i] -= B[j] * x;// }// }//// print(A[h - 1][w - 1], B[h - 1]);// }//// int main() {// #ifndef INTERACTIVE// std::cin.tie(0)->sync_with_stdio(0);// #endif// // std::cout << std::fixed << std::setprecision(12);// int t;// t = 1;// // std::cin >> t;// while (t--) solve();// return 0;// }