BZOJ1179 强连通缩点 + 最长路

题意:图片丢链接

强连通分量内的所有点都可以来回跑,因此只需要在缩点之后的图上跑最长路即可。

国庆第一题,1A开了个好头,以此祈愿…

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;

namespace solver {
    const int maxn = 550000;
    const int maxm = 1100000;
    int n, m;
    int eg[2];
    int head[maxn][2];
    struct A {
        int u, v, w, next;
    } star[maxn][2];
    void add(int u, int v, int w, int type) {
        star[eg[type]][type] = {u, v, w, head[u][type]}, head[u][type] = eg[type]++;
    }
    stack<int> S;
    int dfn[maxn], low[maxn], towhere[maxn], Dindex, id;
    int val[maxn], tv[maxn];
    bool instack[maxn];
    void dfs(int u) {
        dfn[u] = low[u] = ++Dindex;
        S.push(u);
        instack[u] = 1;
        int v;
        for(int i = head[u][0]; ~i; i = star[i][0].next) {
            v = star[i][0].v;
            if(!dfn[v]) {
                dfs(v);
                low[u] = min(low[v], low[u]);
            } else if(instack[v]) {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if(low[u] == dfn[u]) {
            id++;
            for(;;) {
                int tmp = S.top();
                S.pop();
                towhere[tmp] = id;
                instack[tmp] = 0;
                tv[id] += val[tmp];
                if(tmp == u)
                    break;
            }
        }
    }
    void rebuild() {
        for(int i = 1; i <= n; i++) {
            for(int j = head[i][0]; ~j; j = star[j][0].next) {
                int v = star[j][0].v;
                if(towhere[i] == towhere[v]) continue;
                add(towhere[i], towhere[v], 0, 1);
            }
        }
    }
    void init() {
        Dindex = id = 0;
        memset(eg, 0, sizeof eg);
        memset(head, -1, sizeof head);
    }
    int s, p;
    int dis[maxn], vis[maxn];
    void solve() {
        init();
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            add(u, v, 0, 0);
        }
        for(int i = 1; i <= n; i++) {
            int v;
            scanf("%d", &val[i]);
        }
        for(int i = 1; i <= n; i++)
            if(!dfn[i])
                dfs(i);
        rebuild();
        scanf("%d%d", &s, &p);
        queue<int>q;
        q.push(towhere[s]);
        memset(dis, -0x3f3f3f3f, sizeof dis);
        dis[towhere[s]] = tv[towhere[s]];
        while(!q.empty()) {
            int u = q.front();
            q.pop();
            vis[u] = 0;
            for(int j = head[u][1]; ~j; j = star[j][1].next) {
                int v = star[j][1].v;
                if(dis[v] < dis[u] + tv[v]) {
                    dis[v] = dis[u] + tv[v];
                    if(!vis[v]) {
                        q.push(v);
                        vis[v] = 1;
                    }
                }
            }
        }
        int ans = -0x3f3f3f3f;
        for(int i = 1; i <= p; i++) {
            int tmp;
            scanf("%d", &tmp);
            ans = max(ans, dis[towhere[tmp]]);
        }
        printf("%d\n", ans);
    }
}

int main() {
    solver::solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值