結果

問題 No.2985 May Count Induced C4 Subgraphs
ユーザー 👑 rin204
提出日時 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
権限があれば一括ダウンロードができます

ソースコード

diff #
プレゼンテーションモードにする

// #pragma GCC target("avx2")
// #pragma GCC optimize("O3")
// #pragma GCC optimize("unroll-loops")
// #define INTERACTIVE
#include <bits/stdc++.h>
using namespace std;
namespace templates {
// type
using 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 value
const 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)
// function
vector<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 output
namespace io {
// __int128_t
std::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;
}
// tuple
template <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 functions
void 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);
else
print(B);
return f;
}
template <class... T>
void inp(T &...a) {
(cin >> ... >> a);
}
} // namespace io
using namespace io;
// read graph
vector<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 / no
namespace yesno {
// yes
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;
}
inline bool YES(bool f = true) {
cout << (f ? "YES" : "NO") << endl;
return f;
}
// no
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;
}
inline bool NO(bool f = true) {
cout << (!f ? "YES" : "NO") << endl;
return f;
}
// possible
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;
}
inline bool POSSIBLE(bool f = true) {
cout << (f ? "POSSIBLE" : "IMPOSSIBLE") << endl;
return f;
}
// impossible
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;
}
inline bool IMPOSSIBLE(bool f = true) {
cout << (!f ? "POSSIBLE" : "IMPOSSIBLE") << endl;
return f;
}
// Alice Bob
inline 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 Aoki
inline 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 yesno
using namespace yesno;
} // namespace templates
using namespace templates;
/// 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
template <int MOD>
struct Modint {
int x;
Modint() : x(0) {}
Modint(int64_t y) {
if (y >= 0)
x = y % MOD;
else
x = (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;
else
x = (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);
else
return 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. 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;
}
// // #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;
// }
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
0