一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
二、解题报告
1、思路分析
怎么想到先按边双连通分量缩点再计数呢?
一条边按一个方向走后另一个方向走不了,想到桥,而且对于EBCC来说,一个连通块内因为边能走任意次,所以一个连通块可以任意染色
那么我们按照EBCC缩点后,由于起点定了是0,所以得到了一颗以0所在连通块为根的有向树
对于一个根节点u而言,走了一个孩子结点,其它就不能走了,所以孩子结点的染色方案间是加法关系而非乘法关系
我们设u所在子树方案为f(u)
则 f(u) = (1 + Σ(f(v) - 1)) * sz[u] ^ (k + 1)
初值为1是全不染色的情况,-1是减去了全不染色的情况,避免重复计算
2、复杂度
时间复杂度: O(N + M + NlogU),log来自快速幂 空间复杂度:O(N + M)
3、代码详解
#include <bits/stdc++.h>
// #include <ranges>
// #define DEBUG
using i64 = long long;
using u32 = unsigned;
using u64 = unsigned long long;
constexpr int inf32 = 1E9 + 7;
constexpr i64 inf64 = 1E18 + 7;
constexpr double eps = 1E-9;
constexpr int P = 998244353;
int power (int a, i64 b, int p = P) {
int res = 1;
for (; b; b >>= 1, a = 1LL * a * a % p)
if (b & 1)
res = 1LL * res * a % p;
return res;
}
struct DSU {
std::vector<int> p;
int n, sz;
DSU(int _n) : p(_n, -1), n(_n), sz(_n) {}
void init () {
p.assign(n, -1);
}
int find(int x) {
return p[x] < 0 ? x : p[x] = find(p[x]);
}
bool merge(int x, int y) {
int px = find(x), py = find(y);
if (px == py) return false;
if (p[px] > p[py]) std::swap(px, py);
p[px] += p[py], p[py] = px;
-- sz;
return true;
}
int size(int x) {
return -p[find(x)];
}
int size() const {
return sz;
}
};
struct EBCC {
int n;
std::vector<std::vector<int>> adj;
std::vector<int> st;
std::vector<int> dfn, low, bel;
std::vector<std::pair<int, int>> bri;
int cur, cnt;
EBCC() {}
EBCC(int _n) {
init(_n);
}
void init(int _n) {
n = _n;
adj.assign(n, {});
dfn.assign(n, -1);
low.resize(n);
bel.assign(n, -1);
st.clear();
cur = cnt = 0;
}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void dfs(int u, int p) {
dfn[u] = low[u] = cur ++;
st.push_back(u);
for (int v : adj[u]) {
if (v == p) continue;
if (dfn[v] == -1) {
dfs(v, u);
if (low[v] > dfn[u])
bri.emplace_back(u, v);
low[u] = std::min(low[u], low[v]);
}
else if (bel[v] == -1) {
low[u] = std::min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
int v;
do {
v = st.back();
st.pop_back();
bel[v] = cnt;
} while (u != v);
++ cnt;
}
}
std::vector<int> work() {
for (int i = 0; i < n; ++ i) {
if (bel[i] == -1)
dfs(i, -1);
}
return bel;
}
};
void solve() {
int n, m, k;
std::cin >> n >> m >> k;
EBCC ebcc(n);
for (int i = 0, u, v; i < m; ++ i) {
std::cin >> u >> v;
-- u, -- v;
ebcc.addEdge(u, v);
}
std::vector<int> bel = ebcc.work();
int cnt = ebcc.cnt;
std::vector<std::vector<int>> adj(cnt);
std::vector<int> sz(cnt);
DSU dsu(cnt);
for (int i = 0; i < n; ++ i) {
for (int j : ebcc.adj[i]) {
if (bel[i] != bel[j] && dsu.merge(bel[i], bel[j])) {
adj[bel[i]].push_back(bel[j]);
adj[bel[j]].push_back(bel[i]);
}
}
++ sz[bel[i]];
}
auto dfs = [&](auto &&self, int u, int fa) -> int {
int res = 1;
for (int v : adj[u]) {
if (v == fa) continue;
res = (1LL * res + self(self, v, u) - 1) % P;
}
return 1LL * power(k + 1, sz[u]) * res % P;
};
std::cout << dfs(dfs, bel[0], -1);
}
auto FIO = []{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
} ();
int main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int t = 1;
// std::cin >> t;
while (t --)
solve();
return 0;
}