Codeforces Round #261 (Div. 2) - E (459E)
题目连接:http://codeforces.com/contest/459/problem/E
题目大意:给定一张有向图,无自环无重边,每条边有一个边权,求最长严格上升路径长度。(1≤n,m≤3 *10^5)
初见此题觉得以边为点,以点为边,重建一张图,小边权向(通过点)相邻的大边权连边,然后得到一张DAG,跑最长DAG路即可。然而仔细一想,这样新图边数上限可以达到m^2。被否决。
后来想到边权有序化思想(并不知道怎么想到的…可能受kruskal思想影响),按边从大到小排序,然后一条边一条边加入,对于u->v这样一条边,dp[u] = max(dp[u], dp[v] + 1),之所以从大到小排是为了保证状态转移的正确性(与上升路径有关),实际上也就确定了dp的顺序。特殊些的是,对于边权相同的边,不能逐条加入,因为题目要求严格单增,边权相等的时候先加的可能影响后加的转移正确性。因此把边权相同的边同时更新同时加入(用另外一个数组先记下值,再更新dp)。最后答案就是所有点的dp值中取最大。
#include <cstdio> #include <cstdlib> #include <cstring> #include <vector> #include <algorithm> #include <cmath> using namespace std; const int MaxN = 3 * 1e5; struct EDGE { int u, v, w; }edge[MaxN + 5]; int n, m, res = 0, ans[MaxN + 5], t[MaxN + 5]; bool cmp(EDGE x, EDGE y) { return x.w > y.w; } void Init() { scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w); sort(edge + 1, edge + m + 1, cmp); } void Solve() { memset(ans, 0, sizeof(ans)); int head = 1, tail; while (head <= m) { tail = head; while (tail <= m - 1 && edge[tail + 1].w == edge[tail].w) tail++; for (int i = head; i <= tail; i++) t[edge[i].u] = ans[edge[i].u]; for (int i = head; i <= tail; i++) t[edge[i].u] = max(t[edge[i].u], ans[edge[i].v] + 1); for (int i = head; i <= tail; i++) ans[edge[i].u] = t[edge[i].u]; head = tail + 1; } for (int i = 1; i <= n; i++) res = max(res, ans[i]); printf("%d\n", res); } int main() { Init(); Solve(); }