缩点+树形dp,百度之星2024初赛第二场,染色

一、题目

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;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EQUINOX1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值