NOIP2009 T3(Tarjan缩点+DP / 最短路松弛 / DFS / BFS)

这题好多种方法……Orz各路神仙

我先想到的是删与 1 和 n 点都连通的点,然后 Tarjan 缩点之后,枚举每个原图点作为买入点,然后维护和这个点连通的点(scc)权值最大值,减一下就行。
删点 DFS 标记一下,不过要建反图。和这个点连通的点(scc)权值最大值可以用 DP 求得,直接记忆化搜索一下。

然后第二种做法是做两次最短路松弛找到每个点$i$的$1,i$最小值,$i,n$最大值,之后枚举点更新即可。

第三种做法是 DFS。我们发现就只有三种情况,1、找一个点买,2、找一个点卖,3、到终点。
所以每次 DFS 三种情况,用数组可以判重。

第四种做法是 BFS。和DFS差不多。也可以 DP 思想跑 BFS。

第五种做法是 分层图最短路。这里 fy1234567ok 的思路,非常好,也就是把原图分层3个层,第一层是寻找买入点,第二层是寻找售出点,第三层是寻找n点。中间连边代表操作。对于没有贸易活动,直接连到超级n点处。

缩点+DP做法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<stack>
#include<cmath>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace flyinthesky {
const int MAXN = 100000 + 5, MAXM = 500000 + 5;
struct edge {int u, v, nxt; } ed[MAXM * 2], ed_2[MAXM * 2], ed_scc[MAXM * 2];
int n, m, c[MAXN], D[MAXN];
int en, en_2, en_scc, hd[MAXN], hd_2[MAXN], hd_scc[MAXN];
int lnkn[MAXN], lnk1[MAXN];
int low[MAXN], dfn[MAXN], vis[MAXN], sz, scc_sz, scc_belong[MAXN], scc_max[MAXN];
stack<int > s;
void ins(int u, int v) {ed[++en] = (edge){u, v, hd[u]}, hd[u] = en;}
void ins_2(int u, int v) {ed_2[++en_2] = (edge){u, v, hd_2[u]}, hd_2[u] = en_2;}
void ins_scc(int u, int v) {ed_scc[++en_scc] = (edge){u, v, hd_scc[u]}, hd_scc[u] = en_scc;}
void dfs1(int u) { // dfs on ed_2
for (int i = hd_2[u]; i > 0; i = ed_2[i].nxt) {
edge &e = ed_2[i];
if (!lnkn[e.v]) lnkn[e.v] = 1, dfs1(e.v);
}
}
void dfs2(int u) { // dfs on ed
for (int i = hd[u]; i > 0; i = ed[i].nxt) {
edge &e = ed[i];
if (!lnk1[e.v]) lnk1[e.v] = 1, dfs2(e.v);
}
}
void tarjan(int u) { // tarjan on ed
//if (!lnk1[u] || !lnkn[u]) return ;
low[u] = dfn[u] = ++sz, vis[u] = -1, s.push(u);
for (int i = hd[u]; i > 0; i = ed[i].nxt) {
edge &e = ed[i];
if (!vis[e.v]) {
//if (!lnk1[u] || !lnkn[u]) continue ;
tarjan(e.v);
low[u] = min(low[u], low[e.v]);
} else if (vis[e.v] == -1) low[u] = min(low[u], dfn[e.v]);
}
if (dfn[u] == low[u]) {
int e; ++scc_sz;
do {
e = s.top(); s.pop();
scc_belong[e] = scc_sz;
scc_max[scc_sz] = max(c[e], scc_max[scc_sz]);
vis[e] = 1;
} while (e != u);
}
}
int DP(int u) { ///dp on ed_scc
if (D[u] >= 0) return D[u];
int tmp = scc_max[u];
for (int i = hd_scc[u]; i > 0; i = ed_scc[i].nxt) {
edge &e = ed_scc[i];
tmp = max(tmp, DP(e.v));
}
return D[u] = tmp;
}
void clean() {
scc_sz = sz = en = en_2 = en_scc = 0, ms(hd, 0), ms(hd_2, 0), ms(hd_scc, 0);
ms(scc_max, 0), ms(D, -1), ms(vis, 0), ms(scc_belong, 0), ms(lnk1, 0), ms(lnkn, 0);
}
int solve() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%d", &c[i]);
clean();
for (int x, y, z, i = 1; i <= m; ++i) {
scanf("%d%d%d", &x, &y, &z);
if (z == 1) ins(x, y), ins_2(y, x);
else ins(x, y), ins(y, x), ins_2(x, y), ins_2(y, x);
}
lnkn[n] = 1, dfs1(n);
lnk1[1] = 1, dfs2(1);
if(!lnkn[1]) return printf("0\n"), 0;
// for (int i = 1; i <= n; ++i) printf("%d ", lnk1[i]);
// putchar('\n');
// for (int i = 1; i <= n; ++i) printf("%d ", lnkn[i]);
for (int u = 1; u <= n; ++u) if (!lnk1[u] || !lnkn[u]) vis[u] = 1;
tarjan(1);
// for (int i = 1; i <= n; ++i) printf("%d ", scc_belong[i]);
for (int u = 1; u <= n; ++u) {
if (!lnk1[u] || !lnkn[u]) continue ;
for (int i = hd[u]; i > 0; i = ed[i].nxt) {
edge &e = ed[i];
if (scc_belong[u] != scc_belong[e.v]) ins_scc(scc_belong[u], scc_belong[e.v]);
}
}
DP(scc_belong[1]);
// for (int i = 1; i <= scc_sz; ++i) printf("%d ", D[i]);
int ans = 0;
for (int st = 1; st <= n; ++st) ans = max(ans, D[scc_belong[st]] - c[st]);
printf("%d\n", ans);
return 0;
}
}
int main() {
flyinthesky::solve();
return 0;
}

------ 本文结束 ------