luogu3387 缩点

来源:互联网 发布:思快奇软件下载 编辑:程序博客网 时间:2024/05/22 11:30

题目描述

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

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

输入输出格式

输入格式:

第一行,n,m

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

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

输出格式:

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

输入输出样例

输入样例#1:
2 21 11 22 1
输出样例#1:
2






说明

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

①Tarjan缩点

②拓扑或spfa做最远路

Tarjan缩点可以通过构造虚点imp来实现

再开一个数组/开两倍

#include<bits/stdc++.h>#define maxn 100005#define maxm 1000005using namespace std;template <typename T> void read(T &x){x=0;int f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;for(;isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+ch-'0';x*=f;}int n,m,ans,val[maxn*2];int head[maxn*2],to[maxm*2],nxt[maxm*2],tot;int dfn[maxn],low[maxn],dfs_num,st[maxn],top;int g[maxn],imp,rd[maxn*2];int que[maxn],h,tail;int dis[maxn*2];bool vis[maxn], flag[maxn*2];void add(int u,int v){++tot;to[tot]=v;nxt[tot]=head[u];head[u]=tot;}void tarjan(int x){dfn[x]=low[x]=++dfs_num;st[++top]=x;vis[x]=1;for(int e=head[x];e;e=nxt[e]){if(!dfn[to[e]]){tarjan(to[e]);low[x]=min(low[x],low[to[e]]);}else if(vis[to[e]]) low[x]=min(low[x],dfn[to[e]]);}if(dfn[x]==low[x]){++imp;int j;do{j=st[top--];g[j]=imp;val[imp]+=val[j];vis[j]=0;}while(j!=x);}}void topo(){for(int i=n+1;i<=imp;++i) if(rd[i]==0) que[++tail]=i;while(h<tail){++h;int u=que[h];for(int e=head[u];e;e=nxt[e]){if(!flag[to[e]]){rd[to[e]]--;if(rd[to[e]]==0){que[++tail]=to[e];flag[to[e]]=1;}}dis[to[e]]=max(dis[to[e]],dis[u]+val[to[e]]);ans=max(ans,dis[to[e]]);}}}int main() {    read(n),read(m);    imp=n;    for(int i=1;i<=n;++i) read(val[i]);    for(int i=1;i<=m;++i){    int u,v;    read(u),read(v);    add(u,v);}for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);for(int i=1;i<=n;++i){for(int e=head[i];e;e=nxt[e]){if(g[i]==g[to[e]]) continue;else{add(g[i],g[to[e]]);++rd[g[to[e]]];}}}for(int i=n+1;i<=imp;++i) dis[i]=val[i],ans=max(ans,dis[i]);topo();    cout<<ans<<endl;    return 0;}



原创粉丝点击