P3387 【模板】缩点
P3387 【模板】缩点
题目背景
缩点+DP
题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入输出格式
输入格式:
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
输出格式:
共一行,最大的点权之和。
输入输出样例
说明
n<=10^4,m<=10^5,|点权|<=1000 算法:Tarjan缩点+DAGdp
分析
缩点成有点无环图(树),然后拓扑+dp
code
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 #include<cstdlib> 6 7 using namespace std; 8 9 const int N = 20100; 10 const int M = 200100; 11 struct Edge{ 12 int to,nxt; 13 }e[M],s[M]; // 不要混用!!! 14 int head[N],dfn[N],low[N],st[N],bel[N],h[N]; 15 int ru[N],q[1000000],L,R,w[N],sw[N],ans[N]; 16 bool vis[N]; 17 int cnt,tn,tot,top,tot1; 18 19 inline char nc() { 20 static char buf[100000],*p1 = buf,*p2 = buf; 21 return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2) ? EOF : *p1++; 22 } 23 inline int read() { 24 int x = 0,f = 1;char ch = nc(); 25 for (; ch<'0'||ch>'9'; ch = nc()) 26 if (ch=='-') f = -1; 27 for (; ch>='0'&&ch<='9'; ch = nc()) 28 x = x*10+ch-'0'; 29 return x * f; 30 } 31 32 inline void add_edge(int u,int v) { 33 e[++tot].to = v,e[tot].nxt = head[u],head[u] = tot; 34 } 35 inline void add(int u,int v) { 36 s[++tot1].to = v,s[tot1].nxt = h[u],h[u] = tot1; // 不要写成e 37 } 38 void tarjan(int u) { 39 dfn[u] = low[u] = ++tn; 40 st[++top] = u; 41 vis[u] = true; 42 for (int i=head[u]; i; i=e[i].nxt) { 43 int v = e[i].to; 44 if (!dfn[v]) { 45 tarjan(v); 46 low[u] = min(low[u],low[v]); 47 } 48 else if (vis[v]) 49 low[u] = min(low[u],dfn[v]); 50 } 51 if (low[u]==dfn[u]) { 52 ++cnt; 53 do { 54 vis[st[top]] = false; 55 bel[st[top]] = cnt; 56 sw[cnt] += w[st[top]]; 57 top--; 58 } while (st[top+1] != u); 59 } 60 } 61 62 int main() { 63 64 int n = read(),m = read(); 65 for (int i=1; i<=n; ++i) 66 w[i] = read(); 67 for (int i=1; i<=m; ++i) { 68 add_edge(read(),read()); 69 } 70 for (int i=1; i<=n; ++i) 71 if (!dfn[i]) tarjan(i); 72 for (int u=1; u<=n; ++u) { 73 for (int i=head[u]; i; i=e[i].nxt) { 74 int v = e[i].to; 75 if (bel[u]!=bel[v]) 76 add(bel[u],bel[v]),ru[bel[v]]++; 77 } 78 } 79 L = 1,R = 0; 80 for (int i=1; i<=cnt; ++i) { 81 if (!ru[i]) q[++R] = i,ans[i] = sw[i]; 82 } 83 84 while (L <= R) { 85 int u = q[L++]; 86 for (int i=h[u]; i; i=s[i].nxt) { 87 int v = s[i].to; 88 ans[v] = max(ans[u]+sw[v],ans[v]); 89 ru[v] --; 90 if (ru[v]==0) q[++R] = v; // 拓扑!!!不是bfs 91 92 } 93 } 94 int mx = 0; 95 for (int i=1; i<=cnt; ++i) { 96 mx = max(mx,ans[i]); 97 } 98 printf("%d",mx); 99 return 0; 100 }