P3387 【模板】缩点

点击打开链接

缩点+DAG上DP

首先求出图中的所有强连通分量,将每一个强连通分量缩成一点,保留连接两个不同的强联通分量的边构成新图。在新图上跑DAG上DP求最长路径即可。

AC代码:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

const int MAXN=100020;
int n,m,stot,fstw[MAXN],w[MAXN],u[MAXN],v[MAXN],from[MAXN],to[MAXN],ru[MAXN],fst[MAXN],nxt[MAXN];
int dfn[MAXN],cnt,low[MAXN],book[MAXN],sta[MAXN],top,b[MAXN],ctot,tsort[MAXN],t,dp[MAXN],ans;

void tarjan(int x)
{
    dfn[x]=++cnt,low[x]=dfn[x],book[x]=1,sta[++top]=x;
    for(int k=fst[x];k>0;k=nxt[k])
    {
        if(dfn[v[k]]&&book[v[k]])
        //为什么要对在栈中的点进行标记:不标记的话你可能用以栈底为根的子树外的点的dfn/low值来更        
        //新自己的dfn/low值,显然这样是不合法的。
            low[x]=min(low[x],dfn[v[k]]);
        //遍历到祖先结点时,根据low数组的定义,用祖结点的dfs序更新low[x]
        if(!dfn[v[k]])
        {
            tarjan(v[k]);
            low[x]=min(low[x],low[v[k]]);
        }
    }
    if(low[x]==dfn[x])
    {
        ctot++;
        while(1)
        {
            b[sta[top]]=ctot,w[ctot]+=fstw[sta[top]],book[sta[top]]=0;
            top--;
            if(sta[top+1]==x)	break;
        }
    }
}

void dfs(int x)
{
    book[x]=1;
    for(int k=fst[x];k>0;k=nxt[k])
        if(!book[to[k]])	dfs(to[k]);
    tsort[--t]=x;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)	scanf("%d",&fstw[i]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u[i],&v[i]);
        nxt[i]=fst[u[i]],fst[u[i]]=i;
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])	 tarjan(i);
    memset(fst,0,sizeof(fst));
    memset(nxt,0,sizeof(nxt));
    memset(book,0,sizeof(book));
    for(int i=1;i<=m;i++)//建新边
    {
        if(b[u[i]]!=b[v[i]])
        {
            stot++,ru[b[v[i]]]++;
            from[stot]=b[u[i]],to[stot]=b[v[i]],ru[to[stot]]++;
            nxt[stot]=fst[from[stot]],fst[from[stot]]=stot;
        }
    }
    t=ctot+1;
    for(int i=1;i<=ctot;i++)//topsort
        if(!ru[i])	dfs(i);
    for(int i=1;i<=ctot;i++)	dp[i]=w[i];
    for(int i=1;i<=ctot;i++)//DAG上DP求最长路径
    {
        int x=tsort[i];
        for(int k=fst[x];k>0;k=nxt[k])	dp[to[k]]=max(dp[to[k]],dp[x]+w[to[k]]);
    }
    for(int i=1;i<=ctot;i++)	ans=max(ans,dp[i]);
    cout<<ans;
    return 0;
}

 

posted on 2018-09-15 19:52  cgqzsyc  阅读(10)  评论(0)    收藏  举报