P3387 【模板】缩点

    科技2026-09-25  27

    文章目录

    R e s u l t Result Result H y p e r l i n k Hyperlink Hyperlink D e s c r i p t i o n Description Description S o l u t i o n Solution Solution C o d e Code Code

    R e s u l t Result Result


    H y p e r l i n k Hyperlink Hyperlink

    https://www.luogu.com.cn/problem/P3387


    D e s c r i p t i o n Description Description

    给定一张 n n n个点, m m m条边的有向图,点有点权 找出一条路径使得经过的点的权值和最大,点和边可以重复经过,但是只算一次贡献

    数据范围: n ≤ 1 0 4 , m ≤ 1 0 5 n\leq 10^4,m\leq 10^5 n≤104,m≤105


    S o l u t i o n Solution Solution

    容易发现每个环如果选的话,这个环都会直接选掉,所以我们可以把每个环缩成一个点,它的点权就重新定义成所有点的取值和

    接下来就是一个简单的 D A G d p DAGdp DAGdp了,设 f i f_i fi​表示以 i i i为终点的最大路径和,显然有 f j = m a x { f i } + v j f_j=max\{f_i\}+v_j fj​=max{fi​}+vj​,按照拓扑序转移即可

    时间复杂度: O ( n + m ) O(n+m) O(n+m)


    C o d e Code Code

    #include<queue> #include<cctype> #include<cstdio> #include<cstring> #include<algorithm> #define N 10010 #define M 200010 #define LL long long using namespace std;int n,m,v[N],le[N],lg[N],tote,totg,a,b,rd[N],f[N]; struct node{int next,to;}e[M],g[M]; inline void adde(int u,int v){e[++tote]=(node){le[u],v};le[u]=tote;return;} inline void addg(int u,int v){g[++totg]=(node){lg[u],v};lg[u]=totg;return;} bool vis[N]; inline LL read() { char c;LL d=1,f=0; while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48; while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48; return d*f; } int low[N],dfn[N],stk[N],top,cnt,which[N]; inline void Tarjan(int x) { low[x]=dfn[x]=++cnt; stk[++top]=x;vis[x]=true; for(register int i=le[x];i;i=e[i].next) { int y=e[i].to; if(dfn[y]==0) { Tarjan(y); low[x]=min(low[x],low[y]); } else if(vis[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]) { int y; while(y=stk[top--]) { which[y]=x;vis[y]=false; if(x==y) break; v[x]+=v[y]; } return; } } signed main() { n=read();m=read(); for(register int i=1;i<=n;i++) v[i]=read(); for(register int i=1;i<=m;i++) a=read(),b=read(),adde(a,b); for(register int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i); for(register int x=1;x<=n;x++) for(register int i=le[x];i;i=e[i].next) { int y=e[i].to; if(which[x]!=which[y]) addg(which[x],which[y]),rd[which[y]]++; } queue<int>q; for(register int i=1;i<=n;i++) if(which[i]==i&&rd[i]==0) q.push(i),f[i]=v[i]; while(q.size()) { int x=q.front();q.pop(); for(register int i=lg[x];i;i=g[i].next) { int y=g[i].to; f[y]=max(f[y],f[x]+v[y]); if(--rd[y]==0) q.push(y); } } int res=0; for(register int i=1;i<=n;i++) res=max(res,f[i]); printf("%d",res); }
    Processed: 0.009, SQL: 9