Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

P2446 [SDOI2010]大陆争霸

P2446 [SDOI2010]大陆争霸

一个点只有在所有前置点都被经过后才能经过,即该点的 $dis$ 需要被所有的前置点取最大值,并且之后才能用于转移其他点。所以我们在做最短路时,每到达一个点,如果这个点 $t$ 是点 $i$ 的最后一个前置点,那么 $i$ 之后就是可用的了,用 $ dis _ t$ 对 $dis _ i$ 取最大值,而由于堆的性质,点 $t$ 一定是点 $i$ 的所有前置点中 $dis$ 最大的一个了。然后正常转移,但是只有目标点可用了才能放入堆中。

查看代码
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
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
typedef long long LL;
typedef pair <LL, int> PLI;
template <class Type>
void read(Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + c - '0';
flag && (x = ~x + 1);
}
template <class Type>
void write(Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar(x % 10 + '0');
}
const int N = 3e3 + 10;
const LL inf = 7e12;
bool vis[N];
int n, m, s[N];
LL d[N];
struct Edge
{
int v, w;
};
vector <int> f[N];
vector <Edge> g[N];
int main ()
{
read(n), read(m);
for (int a, b, c; m; m--)
{
read(a), read(b), read(c);
g[a].push_back((Edge){b, c});
}
for (int i = 1; i <= n; i++)
{
read(s[i]);
for (int j = 1, a; j <= s[i]; j++)
{
read(a);
f[a].push_back(i);
}
}
priority_queue <PLI, vector <PLI>, greater<PLI>> q;
for (int i = 1; i <= n; i++)
d[i] = inf;
d[1] = 0;
q.push(make_pair(0, 1));
while (!q.empty())
{
int t = q.top().second;
q.pop();
if (vis[t])
continue;
vis[t] = true;
for (int i : f[t])
!--s[i] && (q.push(make_pair(d[i] = max(d[i], d[t]), i)), 0);
for (Edge i : g[t])
if (d[t] + i.w < d[i.v])
{
d[i.v] = d[t] + i.w;
!s[i.v] && (q.push(make_pair(d[i.v], i.v)), 0);
}
}
write(d[n]);
return 0;
}