洛谷 P3387 【模板】缩点
题目背景
缩点+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 <vector> const int N = 1e5; using namespace std; vector<int>G[N],E[N]; bool instack[N]; int ans,Map[N/10][N/10],n,m,tim,Dq[N],dfn[N],low[N],stack[N],top,col[N],sumcol,f[N]; int sec(int a,int b,int opt) {return opt?(a>b?a:b):(a>b?b:a);} void tarjan(int x) { instack[x]=true; stack[++top]=x; low[x]=dfn[x]=++tim; for(int i=0;i<G[x].size();++i) { int v=G[x][i]; if(!dfn[v]) { tarjan(v); low[x]=sec(low[x],low[v],0); } else if(instack[v]) low[x]=sec(low[x],dfn[v],0); } if(low[x]==dfn[x]) { sumcol++; int k; do { k=stack[top--]; col[k]=sumcol; instack[k]=false; f[sumcol]+=Dq[k]; }while(k!=x); } } void dfs(int x,int y) { ans=sec(ans,y,1); for(int i=0;i<E[x].size();++i) dfs(E[x][i],y+f[E[x][i]]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&Dq[i]); for(int u,v;m--;) { scanf("%d%d",&u,&v); G[u].push_back(v); } for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;++i) { for(int j=0;j<G[i].size();++j) { int v=G[i][j]; if(col[i]!=col[v]&&!Map[col[i]][col[v]]) { Map[col[i]][col[v]]=1; E[col[i]].push_back(col[v]); } } } for(int i=1;i<=sumcol;++i) dfs(i,f[i]); printf("%d\n",ans); return 0; }
我们都在命运之湖上荡舟划桨,波浪起伏着而我们无法逃脱孤航。但是假使我们迷失了方向,波浪将指引我们穿越另一天的曙光。