P3387 【模板】縮點
大意:n個圖,m條邊,每個點都有值a[i],求路徑經過的點權值之和最大
思路:因為會出現環 Tarjan 求scc 記錄din dout 然後拓撲排序,dp[v]是經過v點的最大值,每經過一個點就記錄經過他的最大值 然後取max
#include <cstdio>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#define ep emplace_back
#define lld long long
#define ios std::ios::sync_with_stdio(false);std::cin.tie(0);
#define vec vector
const int N = 2e5+9;
const int INF = 0x7FFFFFFF; //2147483647
const int inf1 = 0x3f3f3f3f; //1061109567
const int inf2 = 0x7f7f7f7f; //2139062143 memset賦值用
using namespace std;
int head[N],idx=0;
int head__[N],idx__=0;
bool vis[N];
struct node{
int to,val,next;
}e[N],ee[N];
int n,m;
int a[N],scc_val[N];
//a[n] 每個點的權值
//scc_val[n] 每個scc的權值之和
int time__,low[N],dfn[N];
stack<int>st;
lld ans=0;
lld dp[N];
int din[N],dout[N];
int scc[N],scc_cnt,scc_count[N];
void add(int u,int v,int val){
e[idx] = {v,val,head[u]};
head[u] = idx++;
}
void add__(int u,int v,int val){
ee[idx__] = {v,val,head__[u]};
head__[u] = idx__++;
}
void bd(){
cin>>n>>m;
memset(head,-1,sizeof(head));
memset(head__,-1,sizeof(head__));
for(int i=1 ; i<=n ; ++i)
cin>>a[i];
for(int i=1 ; i<=m ; ++i){
int u,v;
cin>>u>>v;
add(u,v,0);
}
}
void tarjan(int u){
dfn[u] = low[u] = ++time__;
vis[u] = 1;
st.push(u);
for(int i=head[u] ; i!=-1 ; i=e[i].next){
int v =e[i].to;
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(vis[v]){
low[u] = min(low[u],dfn[v]);
}
}
if(low[u] == dfn[u]){
scc_cnt++;
while(1){
int v = st.top();
st.pop();
vis[v] = 0;
scc[v] = scc_cnt;
scc_val[scc_cnt]+=a[v];
if(v== u) break;
}
}
}
void bd__(){
for(int i=1 ; i<=n ; ++i){
for(int u = head[i] ; u!=-1; u = e[u].next){
int v = e[u].to;
int scc_u = scc[i];
int scc_v = scc[v];
if(scc_u != scc_v){
add__(scc_u,scc_v,0);
din[scc_v]++;
dout[scc_u]++;
}
}
}
}
void topo(){
queue<int>q;
// 初始化佇列,將所有入度為0的強連通分量入隊
for(int i=1 ; i<=scc_cnt ; ++i){
if(din[i]==0){
q.push(i);
dp[i] = scc_val[i]; // 初始時,dp[i] 等於該強連通分量的權值之和
}
}
// 進行拓撲排序
while(!q.empty()){
int u = q.front();
q.pop();
for(int i = head__[u] ; i != -1 ; i = ee[i].next){
int v = ee[i].to;
// 更新 dp[v],確保其值為經過當前點u的最大路徑和
dp[v] = max(dp[v], dp[u] + scc_val[v]);
--din[v];
// 如果 v 的入度變為 0,則將 v 入隊
if(din[v] == 0){
q.push(v);
}
}
}
}
int main(){
ios;
bd();
for(int i=1 ; i<=n ; ++i)
if(!dfn[i])
tarjan(i);
bd__();
topo();
for(int i=1 ; i<=scc_cnt ; ++i)
ans = max(ans,dp[i]);
cout<<ans;
return 0;
}