P3387 【模板】缩点

P3387 【模板】缩点

题目描述

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

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

输入格式

第一行两个正整数 \(n,m\)

第二行 \(n\) 个整数,其中第 \(i\) 个数 \(a_i\) 表示点 \(i\) 的点权。

第三至 \(m+2\) 行,每行两个整数 \(u,v\),表示一条 \(u\rightarrow v\) 的有向边。

输出格式

共一行,最大的点权之和。

输入输出样例 #1

输入 #1

2 2
1 1
1 2
2 1

输出 #1

2

说明/提示

对于 \(100\%\) 的数据,\(1\le n \le 10^4\)\(1\le m \le 10^5\)\(0\le a_i\le 10^3\)

  • 2024-11-1 添加了 hack 数据
    这道题题要用到拓扑逆序dp,反正我不知道为什么要逆序
#include<iostream>
#include<stack>
#include<vector>
#define int long long
using namespace std;
const int N=1e5+5;
int n,m,a,b,cnt=0,t=0;
vector<int>v[N],ne[N];
int low[N],dfsn[N],scc[N],vis[N],w[N],dp[N],sz[N];
stack<int>s;
int ans=0;
void dfs(int x){
    low[x]=dfsn[x]=++t;
    s.push(x),vis[x]=1;
    for(int y:v[x]){
        if(!dfsn[y]){
            dfs(y);
            low[x]=min(low[x],low[y]);
        }
        else if(vis[y])low[x]=min(low[x],dfsn[y]);
    }
    if(low[x]==dfsn[x]){
        cnt++;
        while(s.top()!=x){
            int y=s.top();
            vis[y]=0;
            scc[y]=cnt;
            s.pop();
        }
        int y=s.top();
            vis[y]=0;
            scc[y]=cnt;
            s.pop();
    }
}
signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>w[i];
    while(m--){
        cin>>a>>b;
        v[a].push_back(b);
    }
    for(int i=1;i<=n;i++)if(!dfsn[i])dfs(i);
    for(int x=1;x<=n;x++){
        sz[scc[x]]+=w[x];
        for(int y:v[x]){
            if(scc[x]!=scc[y]){
                ne[scc[x]].push_back(scc[y]);
            }
        }
    }
    for(int x=cnt;x>=1;x--){
        if(dp[x]==0)dp[x]=sz[x];
        for(int y:ne[x]){
            dp[y]=max(dp[y],dp[x]+sz[y]);
        }
    }
    for(int i=1;i<=cnt;i++)ans=max(ans,dp[i]);
    cout<<ans;
    return 0;
}
posted @ 2025-03-16 20:12  郭轩均  阅读(24)  评论(0)    收藏  举报