返回顶部

洛谷 P3387 【模板】缩点 (tarjan+拓扑排序dp)

  • 题意:有一张有向图,每个点都有点权,求某一路径的最大权值,重复经过的点的权值只计算一次.

  • 题解:因为存在环什么的,所以我们直接求肯定不好搞,那么我们可以先进行缩点,当找完一个强连通分量后,将其中的所有点权贡献给缩完后的点,缩完点后可以再建新边,注意这里我为了省空间没有记录两个强连通分量之间的重边,但是问题不大,因为我们在后面拓扑排序的时候可以将这些重边删去,都是一样的.因为只有当某个点入度为\(0\)的时候我们才会更新状态.

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
     
     int a[N];
    vector<int> edge[N];
    vector<int> new_edge[N];
    int dfn[N],low[N],timestamp;
    int stk[N],top;
    bool in_stk[N];
    int id[N],scc_cnt;
    int din[N];
    int res[N];
    
    void tarjan(int u){
        dfn[u]=low[u]=++timestamp;
        stk[++top]=u,in_stk[u]=true;
        for(auto w:edge[u]){
            if(!dfn[w]){
                tarjan(w);
                low[u]=min(low[u],low[w]);
            }
            else if(in_stk[w]) low[u]=min(low[u],dfn[w]);
        }
    
        if(dfn[u]==low[u]){
            ++scc_cnt;
            int y;
            do{
                y=stk[top--];
                in_stk[y]=false;
                id[y]=scc_cnt;
                res[scc_cnt]+=a[y];  //记录某个强连通分量的权值
            }while(y!=u);
        }
    }
     
    int main(){
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        int n,m;
        cin>>n>>m;
        rep(i,1,n) cin>>a[i];
    
        rep(i,1,m){
            int u,v;
            cin>>u>>v;
            edge[u].pb(v);
        }
    
        rep(i,1,n){
            if(!dfn[i]) tarjan(i);
        }
     
        rep(i,1,n){
            for(auto w:edge[i]){
                int u=id[i];
                int v=id[w];
                if(u!=v){
                    new_edge[u].pb(v);
                    din[v]++;
                }
            }
        }
    
        queue<int> q;
        int ans=0;
        rep(i,1,scc_cnt){
            if(!din[i]) q.push(i);
        }
        while(!q.empty()){
            int now=q.front();
            q.pop();
            ans=max(ans,res[now]);
            for(auto w:new_edge[now]){
                din[w]--;
                if(din[w]==0){
                    q.push(w);
                    res[w]+=res[now];
                    ans=max(ans,res[w]);
                }
            }
        }
    
        cout<<ans<<'\n';
        
        return 0;
    }
    
posted @ 2021-03-31 11:45  _Kolibri  阅读(59)  评论(0)    收藏  举报