传送门:https://www.luogu.org/problemnew/show/P3387
首先呢,tarjan找一个图的强连通分量是基于对图的dfs的。这中间开了一个dfn[]代表dfs序,还有个low[]代表该节点在dfs形成的树中能到达的最近的根。然后分情况进行更新(一会儿看我代码吧)。
为了记录一个强联通分量,我们还要在开一个栈来储存当前查找的强连通分量。如果low[x] == dfn[x],那就说明这个点在dfs树种不能往上再爬了,于是就开始弹栈。
对于这道题呢,先用tarjan找出强联通分量,然后缩点建立新图。可以发现,建立的新图是一个拓扑图,所以可以拓扑排序后在图上dp,然而我这么写wa了。翻题解的时候看到了一个更简单的方法:记录每一个点的入度,对于每一个入度为0的点用spfa跑一遍最长路,然后在所有最长路中取一个max即可。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<cctype> 7 #include<vector> 8 #include<stack> 9 #include<queue> 10 using namespace std; 11 #define enter printf(" ") 12 #define space printf(" ") 13 #define Mem(a) memset(a, 0, sizeof(a)) 14 typedef long long ll; 15 typedef double db; 16 const int INF = 0x3f3f3f3f; 17 const db eps = 1e-8; 18 const int maxn = 1e5 + 5; 19 inline ll read() 20 { 21 ll ans = 0; 22 char ch = getchar(), last = ' '; 23 while(!isdigit(ch)) {last = ch; ch = getchar();} 24 while(isdigit(ch)) 25 { 26 ans = ans * 10 + ch - '0'; ch = getchar(); 27 } 28 if(last == '-') ans = -ans; 29 return ans; 30 } 31 inline void write(ll x) 32 { 33 if(x < 0) x = -x, putchar('-'); 34 if(x >= 10) write(x / 10); 35 putchar('0' + x % 10); 36 } 37 38 vector<int> v[maxn]; 39 int n, m, a[maxn]; 40 41 int dfn[maxn], low[maxn], cnt = 0; 42 bool in[maxn]; 43 stack<int> st; 44 int col[maxn], ccol = 0, val[maxn]; 45 void tarjan(int now) 46 { 47 dfn[now] = low[now] = ++cnt; 48 st.push(now); in[now] = 1; 49 for(int i = 0; i < (int)v[now].size(); ++i) 50 { 51 if(!dfn[v[now][i]]) //这个联通块还没找完 52 { 53 tarjan(v[now][i]); 54 low[now] = min(low[now], low[v[now][i]]); 55 } 56 else if(in[v[now][i]]) low[now] = min(low[now], dfn[v[now][i]]); //这个点找到了他所在联通块的祖先节点 57 } 58 if(low[now] == dfn[now]) //开始找出联通块中的所有点 59 { 60 int x; ccol++; //ccol相当于染色种数,也就是有多少个连通分量 61 do 62 { 63 x = st.top(); 64 in[x] = 0; st.pop(); 65 col[x] = ccol; val[ccol] += a[x]; //val[]记录新图上的点的点权 66 }while(x != now); 67 } 68 return; 69 } 70 71 vector<int> v2[maxn]; 72 bool du[maxn]; 73 void newGraph(int now) 74 { 75 for(int i = 0; i < (int)v[now].size(); ++i) 76 { 77 int x = col[now], y = col[v[now][i]]; 78 if(x == y) continue; //别忘了两个点在一个联通块的情况 79 v2[x].push_back(y); 80 du[y] = 1; 81 } 82 return; 83 } 84 85 86 int ans = 0; 87 88 int dis[maxn]; 89 bool vis[maxn]; 90 void spfa(int s) 91 { 92 Mem(vis); 93 for(int i = 1; i <= ccol; ++i) dis[i] = -INF; 94 queue<int> q; 95 q.push(s); vis[s] = 1; 96 dis[s] = val[s]; 97 while(!q.empty()) 98 { 99 int now = q.front(); q.pop(); vis[now] = 0; 100 for(int i = 0; i < (int)v2[now].size(); ++i) 101 { 102 if(dis[now] + val[v2[now][i]] > dis[v2[now][i]]) 103 { 104 dis[v2[now][i]] = dis[now] + val[v2[now][i]]; 105 if(!vis[v2[now][i]]) {q.push(v2[now][i]); vis[v2[now][i]] = 1;} 106 } 107 } 108 } 109 for(int i = 1; i <= ccol; ++i) ans = max(ans, dis[i]); //更新答案 110 } 111 int main() 112 { 113 n = read(); m = read(); 114 for(int i = 1; i <= n; ++i) a[i] = read(); 115 for(int i = 1; i <= m; ++i) 116 { 117 int x = read(), y = read(); 118 v[x].push_back(y); 119 } 120 for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i); //有的点从一个定点出发可能走不到,就都得判断是否走过 121 for(int i = 1; i <= n; ++i) newGraph(i); //建立新图 122 for(int i = 1; i <= ccol; ++i) if(!du[i]) spfa(i); //对于每一个入度为0的点,跑最长路 123 write(ans); enter; 124 return 0; 125 }