zoukankan      html  css  js  c++  java
  • [P3387] 【模板】缩点

    [P3387] 【模板】缩点 - 强连通分量,dp

    Description

    给定一个 (n) 个点 (m) 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

    允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

    Solution

    求强连通分量缩点后按拓扑序 DP

    复习一下 Tarjan 求强连通分量

    DFS 整棵树,对每个点,维护 (dfn[])(low[])

    (dfn[p]) 表示 DFS 到 (p) 时的时间戳,即 (p) 是第几个被 DFS 的点

    (low[p]) 表示从 (p) 点开始,能访问到的点中 (dfn) 最小值

    考虑如何求出 (low[p])

    我们在 DFS 的过程中,维护一个栈,每碰到一个点就压进栈

    如果我们确定了某个点 (q) 属于以 (p) 为 DFS 树根的强连通分量,那么 (q) 会在 (p) 的 DFS 过程结束时被弹栈

    枚举 (p) 的所有孩子 (q),有三种情况

    • (q) 是未访问过的点(记 (vis[q]=0)),则 (low[p]=min(low[p],low[q]))

    • (q) 是已经访问过但未出栈的点(记 (vis[q]=1)),则 (low[p]=min(low[p],dfn[q]))

      因为此时 (low[q]) 可能未被计算出((p-q) 可能是后向边)

    • (q) 是已经出栈的点,这种情况直接忽略

    如何确定一个点 (p) 是 DFS 树根?(dfn[p]=low[p])

    #include <bits/stdc++.h>
    using namespace std;
    
    #define int long long
    
    const int N = 1e6 + 5;
    
    int n, m;
    vector<int> g[N], gscc[N];
    int a[N], dfn[N], low[N], ind, vis[N], bel[N], val[N], f[N];
    stack<int> sta;
    vector<vector<int>> scc;
    
    void dfs(int p)
    {
        dfn[p] = ++ind;
        low[p] = dfn[p];
        vis[p] = 1;
        sta.push(p);
        for (int q : g[p])
        {
            if (vis[q] == 0)
            {
                dfs(q);
                low[p] = min(low[p], low[q]);
            }
            else if (vis[q] == 1)
            {
                low[p] = min(low[p], dfn[q]);
            }
        }
        if (dfn[p] == low[p])
        {
            vector<int> vset;
            while (sta.size() && sta.top() != p)
            {
                vset.push_back(sta.top());
                vis[sta.top()] = 2;
                sta.pop();
            }
            vset.push_back(p);
            vis[p] = 2;
            sta.pop();
            scc.push_back(vset);
        }
    }
    
    void dp(int p)
    {
        for (int q : gscc[p])
        {
            if (f[q] == 0)
                dp(q);
            f[p] = max(f[p], f[q]);
        }
        f[p] += val[p];
    }
    
    signed main()
    {
        ios::sync_with_stdio(false);
    
        cin >> n >> m;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
    
        vector<pair<int, int>> edges;
    
        for (int i = 1; i <= m; i++)
        {
            int t1, t2;
            cin >> t1 >> t2;
            g[t1].push_back(t2);
            edges.push_back({t1, t2});
        }
    
        for (int i = 1; i <= n; i++)
            if (vis[i] == 0)
                dfs(i);
    
        for (int comp_id = 0; comp_id < scc.size(); comp_id++)
        {
            auto vset = scc[comp_id];
            for (auto v : vset)
                bel[v] = comp_id, val[comp_id] += a[v];
        }
    
        // cout << scc.size() << endl;
    
        for (auto edge : edges)
        {
            auto [p, q] = edge;
            p = bel[p];
            q = bel[q];
            if (p != q)
                gscc[q].push_back(p);
        }
    
        for (int i = 0; i < scc.size(); i++)
            if (f[i] == 0)
                dp(i);
        int ans = 0;
        for (int i = 0; i < scc.size(); i++)
            ans = max(ans, f[i]);
    
        cout << ans << endl;
    }
    
  • 相关阅读:
    ubuntu(linux)虚拟主机部署桌面,使用window链接
    扫描shader
    Android Studio快捷键
    eclipse取消空格、等号、分号自动录入
    Libgdx学习记录28——创建Desktop程序
    设计模式19——代理模式
    设计模式18——模板方法
    设计模式17——解释器模式
    设计模式16——工厂模式
    设计模式15——外观模式
  • 原文地址:https://www.cnblogs.com/mollnn/p/14560452.html
Copyright © 2011-2022 走看看