Tarjan缩点模板

题目背景

缩点+DP

题目描述

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入输出格式

输入格式:

第一行,n,m

第二行,n个整数,依次代表点权

第三至m+2行,每行两个整数u,v,表示u->v有一条有向边

输出格式:

共一行,最大的点权之和。

输入输出样例

输入样例#1:
2 2
1 1
1 2
2 1
输出样例#1:
2

说明

n<=10^4,m<=10^5,|点权|<=1000算法:Tarjan缩点+DAGdp

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define For(i,a,b) for(register int i=a;i<=b;++i)
using namespace std;
const int maxx=100010;
int dfn[maxx],low[maxx],st[maxx],in[maxx],c[maxx],cnt=0,c_cnt=0,top=0;
int a[maxx/10],d[maxx/10],f[maxx/10],ans=0,dp[maxx];
bool vis[maxx/10];
struct naonuo2B{
    int be[maxx/10],ne[maxx],to[maxx],e;
    void add(int x,int y){
        to[++e]=y; ne[e]=be[x]; be[x]=e;
    }
}Old,New;

int read(){
    char x;
    while((x=getchar())<'0' || x>'9');
    int u=x-'0';
    while((x=getchar())>='0' && x<='9') u=u*10+x-'0';
    return u;
}
void tarjan(int id){    
    st[++top]=id;
    dfn[id]=low[id]=++cnt;
    in[id]=1;
    for(int i=Old.be[id];i;i=Old.ne[i]){
        int go=Old.to[i];
        if(!dfn[go]){
            tarjan(go);
            low[id]=min(low[go],low[id]);
        }
        else if(in[go])
            low[id]=min(low[id],dfn[go]);
    }
    if(dfn[id]==low[id]){
        in[id]=0;
        c[id]=++c_cnt;
        f[c_cnt]=a[id];
        while(id!=st[top]){
//            printf("%d ",st[top]);
            c[st[top]]=c_cnt;
            f[c_cnt]+=a[st[top]];
            in[st[top]]=0;
            top--;
        }
        top--;
//        printf("%d %d\n",c_cnt,f[c_cnt]);
    }
}
void naohengkaihaochou(int x){
    For(i,1,x){
        for(int j=Old.be[i];j;j=Old.ne[j]){
            int go=Old.to[j];
            if(c[go]!=c[i] && !vis[c[go]]){
                vis[c[go]]=1;
                d[c[go]]++;
                New.add(c[i],c[go]);
//                printf("%d~%d\n",c[i],c[go]);
            }
        }
        for(int j=Old.be[i];j;j=Old.ne[j]){
            int go=Old.to[j];
            if(c[go]!=c[i])
                vis[c[go]]=0;
            }
    }
}
int dfs(int id){
    dp[id]=max(dp[id],f[id]);
    ans=max(dp[id],ans);
    for(int i=New.be[id];i;i=New.ne[i]){
        int go=New.to[i];
        if(dp[go]>=dp[id]+f[go]) continue;
        dp[go]=dp[id]+f[go];
        dfs(go);
    }
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("input.in","r",stdin);
    freopen("output.out","w",stdout);
#endif
    int i,j,k,m,n;
    n=read(); m=read();
    For(i,1,n) a[i]=read();
    
    For(i,1,m){
        j=read(); k=read();
        Old.add(j,k);
    }
    For(i,1,n) if(!dfn[i]) tarjan(i);
    naohengkaihaochou(n);
    For(i,1,c_cnt)
        if(!d[i]){
            dfs(i);
        }
    printf("%d\n",ans);
    return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值