Tarjan+topsort(DP)【P3387】 [模板]縮點
阿新 • • 發佈:2018-11-03
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(); }