結果
| 問題 |
No.1303 Inconvenient Kingdom
|
| ユーザー |
drken1215
|
| 提出日時 | 2025-09-12 21:48:40 |
| 言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 270 ms / 3,000 ms |
| コード長 | 23,778 bytes |
| コンパイル時間 | 3,764 ms |
| コンパイル使用メモリ | 311,700 KB |
| 実行使用メモリ | 7,716 KB |
| 最終ジャッジ日時 | 2025-09-12 23:37:31 |
| 合計ジャッジ時間 | 9,905 ms |
|
ジャッジサーバーID (参考情報) |
judge6 / judge5 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 4 |
| other | AC * 34 |
ソースコード
//
// 除算なし行列式, O(N^4)
//
// reference:
// https://noshi91.hatenablog.com/entry/2020/11/28/115621
//
// verified:
// yukicoder No.1303 Inconvenient Kingdom
// https://yukicoder.me/problems/no/1303
//
#include <bits/stdc++.h>
using namespace std;
//------------------------------//
// Utility
//------------------------------//
template<class S, class T> inline bool chmax(S &a, T b) { return (a < b ? a = b, 1 : 0); }
template<class S, class T> inline bool chmin(S &a, T b) { return (a > b ? a = b, 1 : 0); }
using pint = pair<int, int>;
using pll = pair<long long, long long>;
using tint = array<int, 3>;
using tll = array<long long, 3>;
using fint = array<int, 4>;
using fll = array<long long, 4>;
using qint = array<int, 5>;
using qll = array<long long, 5>;
using vint = vector<int>;
using vll = vector<long long>;
using ll = long long;
using u32 = unsigned int;
using u64 = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
template <class T>
using min_priority_queue = priority_queue<T, vector<T>, greater<T>>;
#define COUT(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl
// debug stream
template<class T1, class T2> ostream& operator << (ostream &s, pair<T1,T2> P)
{ return s << '<' << P.first << ", " << P.second << '>'; }
template<class T> ostream& operator << (ostream &s, array<T, 3> P)
{ return s << '<' << P[0] << ", " << P[1] << ", " << P[2] << '>'; }
template<class T> ostream& operator << (ostream &s, array<T, 4> P)
{ return s << '<' << P[0] << ", " << P[1] << ", " << P[2] << ", " << P[3] << '>'; }
template<class T> ostream& operator << (ostream &s, vector<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, deque<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, vector<vector<T> > P)
{ for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; }
template<class T> ostream& operator << (ostream &s, set<T> P)
{ for (auto it : P) { s << "<" << it << "> "; } return s; }
template<class T> ostream& operator << (ostream &s, multiset<T> P)
{ for (auto it : P) { s << "<" << it << "> "; } return s; }
template<class T> ostream& operator << (ostream &s, unordered_set<T> P)
{ for (auto it : P) { s << "<" << it << "> "; } return s; }
template<class T1, class T2> ostream& operator << (ostream &s, map<T1,T2> P)
{ for (auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; }
template<class T1, class T2> ostream& operator << (ostream &s, unordered_map<T1,T2> P)
{ for (auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; }
// Union-Find
struct UnionFind {
// core member
vector<int> par, nex;
// constructor
UnionFind() { }
UnionFind(int N) : par(N, -1), nex(N) {
init(N);
}
void init(int N) {
par.assign(N, -1);
nex.resize(N);
for (int i = 0; i < N; ++i) nex[i] = i;
}
// core methods
int root(int x) {
if (par[x] < 0) return x;
else return par[x] = root(par[x]);
}
bool same(int x, int y) {
return root(x) == root(y);
}
bool merge(int x, int y, bool merge_technique = true) {
x = root(x), y = root(y);
if (x == y) return false;
if (merge_technique) if (par[x] > par[y]) swap(x, y); // merge technique
par[x] += par[y];
par[y] = x;
swap(nex[x], nex[y]);
return true;
}
int size(int x) {
return -par[root(x)];
}
// get group
vector<int> group(int x) {
vector<int> res({x});
while (nex[res.back()] != x) res.push_back(nex[res.back()]);
return res;
}
vector<vector<int>> groups() {
vector<vector<int>> member(par.size());
for (int v = 0; v < (int)par.size(); ++v) {
member[root(v)].push_back(v);
}
vector<vector<int>> res;
for (int v = 0; v < (int)par.size(); ++v) {
if (!member[v].empty()) res.push_back(member[v]);
}
return res;
}
// debug
friend ostream& operator << (ostream &s, UnionFind uf) {
const vector<vector<int>> &gs = uf.groups();
for (const vector<int> &g : gs) {
s << "group: ";
for (int v : g) s << v << " ";
s << endl;
}
return s;
}
};
//------------------------------//
// General Matrix
//------------------------------//
// modint matrix
template<class T> struct GeneralMatrix {
// inner value
int H, W;
T ZERO = T();
T ONE;
vector<vector<T>> val;
// constructors
GeneralMatrix() : H(0), W(0) {}
GeneralMatrix(int h, int w, const T &ZERO, const T &ONE)
: H(h), W(w), ZERO(ZERO), ONE(ONE), val(h, vector<T>(w, ZERO)) {}
GeneralMatrix(const GeneralMatrix &mat)
: H(mat.H), W(mat.W), ZERO(mat.ZERO), ONE(mat.ONE), val(mat.val) {}
void init(int h, int w, const T &x) {
H = h, W = w;
val.assign(h, vector<T>(w, x));
}
void resize(int h, int w) {
H = h, W = w;
val.resize(h);
for (int i = 0; i < h; ++i) val[i].resize(w);
}
// getter and debugger
constexpr int height() const { return H; }
constexpr int width() const { return W; }
constexpr bool empty() const { return height() == 0; }
vector<T>& operator [] (int i) { return val[i]; }
constexpr const vector<T>& operator [] (int i) const { return val[i]; }
friend constexpr ostream& operator << (ostream &os, const GeneralMatrix<T> &mat) {
for (int i = 0; i < mat.height(); ++i) {
for (int j = 0; j < mat.width(); ++j) {
if (j) os << ' ';
os << mat.val[i][j];
}
os << '\n';
}
return os;
}
// comparison operators
constexpr bool operator == (const GeneralMatrix &r) const {
return this->val == r.val;
}
constexpr bool operator != (const GeneralMatrix &r) const {
return this->val != r.val;
}
// arithmetic operators
constexpr GeneralMatrix& operator += (const GeneralMatrix &r) {
assert(height() == r.height());
assert(width() == r.width());
for (int i = 0; i < height(); ++i)
for (int j = 0; j < width(); ++j)
val[i][j] = val[i][j] + r.val[i][j];
return *this;
}
constexpr GeneralMatrix& operator -= (const GeneralMatrix &r) {
assert(height() == r.height());
assert(width() == r.width());
for (int i = 0; i < height(); ++i)
for (int j = 0; j < width(); ++j)
val[i][j] = val[i][j] - r.val[i][j];
return *this;
}
constexpr GeneralMatrix& operator *= (const T &v) {
for (int i = 0; i < height(); ++i)
for (int j = 0; j < width(); ++j)
val[i][j] = val[i][j] * v;
return *this;
}
constexpr GeneralMatrix& operator *= (const GeneralMatrix &r) {
assert(width() == r.height());
GeneralMatrix<T> res(height(), r.width());
for (int i = 0; i < height(); ++i)
for (int j = 0; j < r.width(); ++j)
for (int k = 0; k < width(); ++k)
res[i][j] = res[i][j] + val[i][k] * r.val[k][j];
return (*this) = res;
}
constexpr GeneralMatrix operator + () const { return GeneralMatrix(*this); }
constexpr GeneralMatrix operator - () const { return GeneralMatrix(*this) = -GeneralMatrix(*this); }
constexpr GeneralMatrix operator + (const GeneralMatrix &r) const { return GeneralMatrix(*this) += r; }
constexpr GeneralMatrix operator - (const GeneralMatrix &r) const { return GeneralMatrix(*this) -= r; }
constexpr GeneralMatrix operator * (const T &v) const { return GeneralMatrix(*this) *= v; }
constexpr GeneralMatrix operator * (const GeneralMatrix &r) const { return GeneralMatrix(*this) *= r; }
constexpr vector<T> operator * (const vector<T> &v) const {
assert(width() == v.size());
vector<T> res(height(), ZERO);
for (int i = 0; i < height(); i++)
for (int j = 0; j < width(); j++)
res[i] += val[i][j] * v[j];
return res;
}
// transpose
constexpr GeneralMatrix trans() const {
GeneralMatrix<T> res(width(), height());
for (int row = 0; row < width(); row++)
for (int col = 0; col < height(); col++)
res[row][col] = val[col][row];
return res;
}
friend constexpr GeneralMatrix<T> trans(const GeneralMatrix<T> &mat) {
return mat.trans();
}
// pow
constexpr GeneralMatrix pow(long long n) const {
assert(height() == width());
GeneralMatrix<T> res(height(), width()), mul(*this);
for (int row = 0; row < height(); ++row) res[row][row] = ONE;
while (n > 0) {
if (n & 1) res = res * mul;
mul = mul * mul;
n >>= 1;
}
return res;
}
friend constexpr GeneralMatrix<T> pow(const GeneralMatrix<T> &mat, long long n) {
return mat.pow(n);
}
// determinant (without division, O(N^4))
constexpr T det() const {
assert(height() == width());
if (height() == 0) return ONE;
int N = height();
vector<vector<T>> dp(N + 1, vector<T>(N + 1, ZERO));
for (int i = 0; i <= N; i++) dp[i][i] = ONE;
for (int step = 0; step < N; step++) {
vector<vector<T>> nex(N + 1, vector<T>(N + 1, ZERO));
for (int row = 0; row < N; row++) {
for (int col = row; col < N; col++) {
for (int col2 = row + 1; col2 < N; col2++) {
nex[row][col2] = nex[row][col2] - dp[row][col] * (*this)[col][col2];
}
T tmp = dp[row][col] * (*this)[col][row];
for (int col2 = row + 1; col2 <= N; col2++) {
nex[col2][col2] = nex[col2][col2] + tmp;
}
}
}
swap(dp, nex);
}
return dp[N][N];
}
friend constexpr T det(const GeneralMatrix<T> &mat) {
return mat.det();
}
// determinant (by Euclidean Algorithm)
constexpr int find_pivot(int cur_rank, int col) const {
int pivot = -1;
for (int row = cur_rank; row < height(); ++row) {
if (val[row][col] != ZERO) {
pivot = row;
break;
}
}
return pivot;
}
constexpr T det_euclid() const {
assert(height() == width());
if (height() == 0) return ONE;
GeneralMatrix<T> A(*this);
int rank = 0;
T res = ONE;
for (int col = 0; col < width(); ++col) {
int pivot = A.find_pivot(rank, col);
if (pivot == -1) return ZERO;
if (pivot != rank) swap(A[pivot], A[rank]), res = -res;
for (int row = rank + 1; row < height(); ++row) {
while (A[row][col] != ZERO) {
swap(A[rank], A[row]), res = -res;
auto quo = A[row][col] / A[rank][col];
for (int col2 = rank; col2 < width(); ++col2) {
A[row][col2] -= A[rank][col2] * quo;
}
}
}
rank++;
}
for (int col = 0; col < height(); ++col) res *= A[col][col];
return res;
}
friend constexpr T det_euclid(const GeneralMatrix<T> &mat) {
return mat.det_euclid();
}
};
//------------------------------//
// mod algorithms
//------------------------------//
// safe mod
template<class T_VAL, class T_MOD>
constexpr T_VAL safe_mod(T_VAL a, T_MOD m) {
assert(m > 0);
a %= m;
if (a < 0) a += m;
return a;
}
// mod pow
template<class T_VAL, class T_MOD>
constexpr T_VAL mod_pow(T_VAL a, T_VAL n, T_MOD m) {
T_VAL res = 1;
while (n > 0) {
if (n % 2 == 1) res = res * a % m;
a = a * a % m;
n >>= 1;
}
return res;
}
// mod inv
template<class T_VAL, class T_MOD>
constexpr T_VAL mod_inv(T_VAL a, T_MOD m) {
T_VAL b = m, u = 1, v = 0;
while (b > 0) {
T_VAL t = a / b;
a -= t * b, swap(a, b);
u -= t * v, swap(u, v);
}
u %= m;
if (u < 0) u += m;
return u;
}
// modint
template<int MOD = 998244353, bool PRIME = true> struct Fp {
// inner value
unsigned int val;
// constructor
constexpr Fp() : val(0) { }
template<std::signed_integral T> constexpr Fp(T v) {
long long tmp = (long long)(v % (long long)(get_umod()));
if (tmp < 0) tmp += get_umod();
val = (unsigned int)(tmp);
}
template<std::unsigned_integral T> constexpr Fp(T v) {
val = (unsigned int)(v % get_umod());
}
constexpr long long get() const { return val; }
constexpr static int get_mod() { return MOD; }
constexpr static unsigned int get_umod() { return MOD; }
// arithmetic operators
constexpr Fp operator + () const { return Fp(*this); }
constexpr Fp operator - () const { return Fp() - Fp(*this); }
constexpr Fp operator + (const Fp &r) const { return Fp(*this) += r; }
constexpr Fp operator - (const Fp &r) const { return Fp(*this) -= r; }
constexpr Fp operator * (const Fp &r) const { return Fp(*this) *= r; }
constexpr Fp operator / (const Fp &r) const { return Fp(*this) /= r; }
constexpr Fp& operator += (const Fp &r) {
val += r.val;
if (val >= get_umod()) val -= get_umod();
return *this;
}
constexpr Fp& operator -= (const Fp &r) {
val -= r.val;
if (val >= get_umod()) val += get_umod();
return *this;
}
constexpr Fp& operator *= (const Fp &r) {
unsigned long long tmp = val;
tmp *= r.val;
val = (unsigned int)(tmp % get_umod());
return *this;
}
constexpr Fp& operator /= (const Fp &r) {
return *this = *this * r.inv();
}
constexpr Fp pow(long long n) const {
assert(n >= 0);
Fp res(1), mul(*this);
while (n) {
if (n & 1) res *= mul;
mul *= mul;
n >>= 1;
}
return res;
}
constexpr Fp inv() const {
if (PRIME) {
assert(val);
return pow(get_umod() - 2);
} else {
assert(val);
return mod_inv((long long)(val), get_umod());
}
}
// other operators
constexpr bool operator == (const Fp &r) const {
return this->val == r.val;
}
constexpr bool operator != (const Fp &r) const {
return this->val != r.val;
}
constexpr bool operator < (const Fp &r) const {
return this->val < r.val;
}
constexpr bool operator > (const Fp &r) const {
return this->val > r.val;
}
constexpr bool operator <= (const Fp &r) const {
return this->val <= r.val;
}
constexpr bool operator >= (const Fp &r) const {
return this->val >= r.val;
}
constexpr Fp& operator ++ () {
++val;
if (val == get_umod()) val = 0;
return *this;
}
constexpr Fp& operator -- () {
if (val == 0) val = get_umod();
--val;
return *this;
}
constexpr Fp operator ++ (int) {
Fp res = *this;
++*this;
return res;
}
constexpr Fp operator -- (int) {
Fp res = *this;
--*this;
return res;
}
friend constexpr istream& operator >> (istream &is, Fp<MOD> &x) {
long long tmp = 1;
is >> tmp;
tmp = tmp % (long long)(get_umod());
if (tmp < 0) tmp += get_umod();
x.val = (unsigned int)(tmp);
return is;
}
friend constexpr ostream& operator << (ostream &os, const Fp<MOD> &x) {
return os << x.val;
}
friend constexpr Fp<MOD> pow(const Fp<MOD> &r, long long n) {
return r.pow(n);
}
friend constexpr Fp<MOD> inv(const Fp<MOD> &r) {
return r.inv();
}
};
// dynamic modint
struct DynamicModint {
using mint = DynamicModint;
// static menber
static int MOD;
// inner value
unsigned int val;
// constructor
DynamicModint() : val(0) { }
template<std::signed_integral T> DynamicModint(T v) {
long long tmp = (long long)(v % (long long)(get_umod()));
if (tmp < 0) tmp += get_umod();
val = (unsigned int)(tmp);
}
template<std::unsigned_integral T> DynamicModint(T v) {
val = (unsigned int)(v % get_umod());
}
long long get() const { return val; }
static int get_mod() { return MOD; }
static unsigned int get_umod() { return MOD; }
static void set_mod(int mod) { MOD = mod; }
// arithmetic operators
mint operator + () const { return mint(*this); }
mint operator - () const { return mint() - mint(*this); }
mint operator + (const mint &r) const { return mint(*this) += r; }
mint operator - (const mint &r) const { return mint(*this) -= r; }
mint operator * (const mint &r) const { return mint(*this) *= r; }
mint operator / (const mint &r) const { return mint(*this) /= r; }
mint& operator += (const mint &r) {
val += r.val;
if (val >= get_umod()) val -= get_umod();
return *this;
}
mint& operator -= (const mint &r) {
val -= r.val;
if (val >= get_umod()) val += get_umod();
return *this;
}
mint& operator *= (const mint &r) {
unsigned long long tmp = val;
tmp *= r.val;
val = (unsigned int)(tmp % get_umod());
return *this;
}
mint& operator /= (const mint &r) {
return *this = *this * r.inv();
}
mint pow(long long n) const {
assert(n >= 0);
mint res(1), mul(*this);
while (n) {
if (n & 1) res *= mul;
mul *= mul;
n >>= 1;
}
return res;
}
mint inv() const {
assert(val);
return mod_inv((long long)(val), get_umod());
}
// other operators
bool operator == (const mint &r) const {
return this->val == r.val;
}
bool operator != (const mint &r) const {
return this->val != r.val;
}
bool operator < (const mint &r) const {
return this->val < r.val;
}
bool operator > (const mint &r) const {
return this->val > r.val;
}
bool operator <= (const mint &r) const {
return this->val <= r.val;
}
bool operator >= (const mint &r) const {
return this->val >= r.val;
}
mint& operator ++ () {
++val;
if (val == get_umod()) val = 0;
return *this;
}
mint& operator -- () {
if (val == 0) val = get_umod();
--val;
return *this;
}
mint operator ++ (int) {
mint res = *this;
++*this;
return res;
}
mint operator -- (int) {
mint res = *this;
--*this;
return res;
}
friend istream& operator >> (istream &is, mint &x) {
long long tmp = 1;
is >> tmp;
tmp = tmp % (long long)(get_umod());
if (tmp < 0) tmp += get_umod();
x.val = (unsigned int)(tmp);
return is;
}
friend ostream& operator << (ostream &os, const mint &x) {
return os << x.val;
}
friend mint pow(const mint &r, long long n) {
return r.pow(n);
}
friend mint inv(const mint &r) {
return r.inv();
}
};
int DynamicModint::MOD;
// Binomial coefficient
template<class mint> struct BiCoef {
vector<mint> fact_, inv_, finv_;
constexpr BiCoef() {}
constexpr BiCoef(int n) : fact_(n, 1), inv_(n, 1), finv_(n, 1) {
init(n);
}
constexpr void init(int n) {
fact_.assign(n, 1), inv_.assign(n, 1), finv_.assign(n, 1);
int MOD = fact_[0].get_mod();
for(int i = 2; i < n; i++){
fact_[i] = fact_[i-1] * i;
inv_[i] = -inv_[MOD%i] * (MOD/i);
finv_[i] = finv_[i-1] * inv_[i];
}
}
constexpr mint com(int n, int k) const {
if (n < k || n < 0 || k < 0) return 0;
return fact_[n] * finv_[k] * finv_[n-k];
}
constexpr mint fact(int n) const {
if (n < 0) return 0;
return fact_[n];
}
constexpr mint inv(int n) const {
if (n < 0) return 0;
return inv_[n];
}
constexpr mint finv(int n) const {
if (n < 0) return 0;
return finv_[n];
}
};
//------------------------------//
// Examples
//------------------------------//
// yukicoder No.1303 Inconvenient Kingdom
using mint = Fp<>;
using Node = pair<mint, mint>;
Node operator + (Node a, Node b) { return Node(a.first + b.first, a.second + b.second); }
Node operator - (Node a, Node b) { return Node(a.first - b.first, a.second - b.second); }
Node operator * (Node a, Node b) { return Node(a.first * b.first, a.first * b.second + a.second * b.first); }
void yukicoder_1303_general_det() {
int N, M, u, v;
cin >> N >> M;
vector G(N, vector(N, 0));
vector degs(N, 0);
UnionFind uf(N);
for (int i = 0; i < M; i++) {
cin >> u >> v, u--, v--;
G[u][v]++, G[v][u]++, degs[u]++, degs[v]++;
uf.merge(u, v);
}
auto calc = [&](const vector<int> &group) -> mint {
vector<int> conv(N, -1);
int iter = 0;
for (auto v : group) conv[v] = iter++;
GeneralMatrix<mint> L(iter, iter, mint(0), mint(1));
for (int i = 0; i < iter; i++) L[i][i] = 1;
for (auto v1 : group) {
int i = conv[v1];
int deg = 0;
for (auto v2 : group) {
if (G[v1][v2]) deg += G[v1][v2];
int j = conv[v2];
if (i < iter - 1 && j < iter - 1) L[i][j] = -G[v1][v2];
}
if (i < iter - 1) L[i][i] = deg;
}
return det(L);
};
auto groups = uf.groups();
if (groups.size() > 1) {
mint res = 1;
vector<long long> siz;
for (auto group : groups) siz.push_back(group.size()), res *= calc(group);
sort(siz.begin(), siz.end(), greater<long long>());
long long sum = siz[0] + siz[1], sum2 = sum * sum;
for (int i = 2; i < (int)siz.size(); i++) sum += siz[i], sum2 += siz[i] * siz[i];
long long huben = (sum * sum - sum2);
if (siz[0] == siz[1]) {
long long sumsiz = 0, sumsiz2 = 0;
for (auto s : siz) if (s == siz[0]) sumsiz += s, sumsiz2 += s * s;
long long fac = (sumsiz * sumsiz - sumsiz2) / 2;
res *= fac;
} else {
long long sum_sub = 0;
for (auto s : siz) if (s == siz[1]) sum_sub += s;
long long fac = siz[0] * sum_sub;
res *= fac;
}
cout << huben << endl << res << endl;
} else {
long long huben = 0;
Node zero(0, 0), one(1, 0);
GeneralMatrix<Node> L(N, N, zero, one);
for (int i = 0; i < N; i++) L[i][i] = one;
for (int i = 0; i < N-1; i++) {
L[i][i] = Node(mint(degs[i]), mint(N - 1 - degs[i]));
for (int j = 0; j < N-1; j++) {
if (i == j) continue;
if (G[i][j]) L[i][j] = Node(mint(-G[i][j]), 0);
else L[i][j] = Node(0, mint(-1));
}
}
Node f = det(L);
mint res = f.first + f.second;
cout << huben << endl << res << endl;
}
}
int main() {
yukicoder_1303_general_det();
}
drken1215