1. 程式人生 > >Tarjan+topsort(DP)【P3387】 [模板]縮點

Tarjan+topsort(DP)【P3387】 [模板]縮點

Description

給定一個n個點m條邊有向圖,每個點有一個權值,求一條路徑,使路徑經過的點權值之和最大。你只需要求出這個權值和。

允許多次經過一條邊或者一個點,但是,重複經過的點,權值只計算一次。

Input

第一行,n,m

第二行,n個整數,依次代表點權

第三至m+2行,每行兩個整數u,v,表示u->v有一條有向邊

Output

共一行,最大的點權之和。

縮點+DP這是題目說的

先縮點,對於每一個聯通塊之間建邊,這時得到一張\(DAG\)(有向無環圖)

我們對其跑拓撲排序,然後開一個數組\(dis\)記錄到達某個點的最大值.

對於那些入度為0的點,我們初始化其\(dis\)

為其聯通塊的點權之和.

然後每次取\(max\)即可.

最終\(ans\)即為對到達每個點的\(dis\)\(max\)

程式碼

#include<cstdio>
#include<iostream>
#include<algorithm>
#define R register

using namespace std;

const int gz=50008;

inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int head[gz],tot,val[gz],v[gz],h[gz],dis[gz],ins[gz],ans,n,m;

struct cod{int u,v;}edge[gz<<1],e[gz<<1];

inline void add(R int x,R int y)
{
    edge[++tot].u=head[x];
    edge[tot].v=y;
    head[x]=tot;
}

inline void ado(R int x,R int y)
{
    e[++tot].u=h[x];
    e[tot].v=y;
    h[x]=tot;
}

int dfn[gz],belong[gz],idx,low[gz],stk[gz],top,col;

bool inq[gz];

void tarjan(R int x)
{
    low[x]=dfn[x]=++idx;
    stk[++top]=x;inq[x]=true;
    for(R int i=head[x];i;i=edge[i].u)
    {
        if(!dfn[edge[i].v])
        {
            tarjan(edge[i].v);
            low[x]=min(low[x],low[edge[i].v]);
        }
        else if(inq[edge[i].v])
            low[x]=min(low[x],dfn[edge[i].v]);
    }
    if(low[x]==dfn[x])
    {
        int now=-1;
        col++;
        while(now!=x)
        {
            now=stk[top--];
            belong[now]=col;
            inq[now]=false;
            v[col]+=val[now];
        }
    }
}

inline void topsort()
{
    top=0;
    for(R int i=1;i<=col;i++)
        if(!ins[i])stk[++top]=i,dis[i]=v[i];
    while(top)
    {
        int u=stk[top--];
        for(R int i=h[u];i;i=e[i].u)
        {
            ins[e[i].v]--;
            dis[e[i].v]=max(dis[e[i].v],dis[u]+v[e[i].v]);
            if(!ins[e[i].v])stk[++top]=e[i].v;
        }
    }
    for(R int i=1;i<=col;i++)
        ans=max(ans,dis[i]);
    printf("%d\n",ans);
}

int main()
{
    in(n),in(m);
    for(R int i=1;i<=n;i++)in(val[i]);
    for(R int i=1,x,y;i<=m;i++)
    {
        in(x),in(y);
        add(x,y);
    }
    for(R int i=1;i<=n;i++)
        if(!dfn[i])tarjan(i);
    tot=0;
    for(R int i=1;i<=n;i++)
        for(R int j=head[i];j;j=edge[j].u)
            if(belong[i]!=belong[edge[j].v])
            {
                ins[belong[edge[j].v]]++;
                ado(belong[i],belong[edge[j].v]);
            }
    topsort();
}