Luogu P3387 【模板】缩点
题目背景
缩点+DP
题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入输出格式
输入格式:
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
输出格式:
共一行,最大的点权之和。
输入输出样例
说明
n<=10^4,m<=10^5,点权<=1000
算法:Tarjan缩点+DAGdp
1 //2018年4月30日17:48:17 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 using namespace std; 6 7 const int N = 100001; 8 const int M = 500001; 9 10 int n, m, w[N], ans; 11 12 int fir[N], to[M], nxt[M], edge_num; 13 void addEdge(int x, int y){ 14 to[++edge_num] = y; 15 nxt[edge_num] = fir[x]; 16 fir[x] = edge_num; 17 } 18 19 int dfn[N], low[N], stack[N], top, instack[N], tim, col_num, color[N], sum[N]; 20 void Tarjan(int x){ 21 low[x] = dfn[x] = ++tim; 22 stack[++top] = x; 23 instack[x] = 1; 24 for(int i=fir[x]; i; i=nxt[i]){ 25 int v = to[i]; 26 if(!dfn[v]){ 27 Tarjan(v); 28 low[x] = min(low[x], low[v]); 29 }else if(instack[v]){ 30 low[x] = min(low[x], dfn[v]); 31 } 32 } 33 if(low[x] == dfn[x]){ 34 col_num++; 35 while(1){ 36 int now = stack[top--]; 37 instack[now] = 0; 38 color[now] = col_num; 39 sum[col_num] += w[now]; 40 if(now == x) break; 41 } 42 } 43 } 44 45 int dp[N]; 46 47 int dfs(int u){ 48 if(dp[u]) return dp[u]; 49 dp[u] = sum[u]; 50 int mx = 0; 51 for(int i=fir[u]; i; i=nxt[i]){ 52 int v = to[i]; 53 if(!dp[v]) dfs(v); 54 if(dp[v] > mx) mx = dp[v]; 55 } 56 dp[u] += mx; 57 return dp[u]; 58 } 59 60 int main(){ 61 scanf("%d%d", &n, &m); 62 for(int i=1; i<=n; i++) 63 scanf("%d", &w[i]); 64 int x[N], y[N]; 65 for(int i=1; i<=m; i++){ 66 scanf("%d%d", &x[i], &y[i]); 67 addEdge(x[i], y[i]); 68 } 69 for(int i=1; i<=n; i++) 70 if(!dfn[i]) 71 Tarjan(i); 72 memset(fir, 0, sizeof(fir)); 73 memset(to, 0, sizeof(to)); 74 memset(nxt, 0, sizeof(nxt)); 75 edge_num = 0; 76 for(int i=1; i<=m; i++) 77 if(color[x[i]] != color[y[i]]) 78 addEdge(color[x[i]], color[y[i]]); 79 for(int i=1; i<=col_num; i++) 80 if(!dp[i]){ 81 dfs(i); 82 ans = max(ans, dp[i]); 83 } 84 printf("%d\n", ans); 85 86 return 0; 87 }