【Tarjan 拓撲排序 dp】P3387 【模板】縮點

phrink發表於2024-08-11

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

相關文章