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


浙公网安备 33010602011771号