题目
题解
由于题目中说如果经过从u到v的传送阵时,在v到u存在路径,那么就要损耗1点灵力值。考虑tarjan的强连通分量,在一个强连通分量中的点,必然可以互达,就会损耗灵力值。跑SPFA时由于有①灵力损耗最小②耗时最小两个限制,所以需要分开进行判断。当两个点处于同一强连通分量时,灵力损耗为dis[u] + 1,如果比dis[v]小就更新灵力和时间,如果相同就再比较时间,单独更新时间。若不处于同一强连通分量,灵力损耗就为dis[u],其余不变。如此就能直接求出最小灵力消耗和耗时。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 300010;
struct Edge {
int v, next, w;
} edge[N];
int n, m, head[N], num = 0,indexx = 0, low[N], dfn[N], top = 0, stack[N], cnt = 0, place[N];
long long dis[N], dist[N];
bool vis[N];
void add(int u, int v, int w) {
num ++;
edge[num].v = v;
edge[num].w = w;
edge[num].next = head[u];
head[u] = num;
}
void tarjan(int u) {
indexx ++;
dfn[u] = indexx;
low[u] = indexx;
vis[u] = true;
stack[++ top] = u;
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if(! dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]) ;
}
else if(vis[v])
low[u] = min(low[u], dfn[v]) ;
}
if(dfn[u] == low[u]) {
vis[u] = false;
place[u] = ++ cnt;
while(stack[top] != u) {
place[stack[top]] = cnt;
vis[stack[top --]] = false;
}
top --;
}
}
deque <int> q;
void spfa() {
memset(vis, 0, sizeof(vis));
memset(dis, 127, sizeof(dis));
memset(dist, 127, sizeof(dist));
q.push_back(1);
vis[1] = true; dis[1] = dist[1] = 0;
while(! q.empty()) {
int u = q.front();
q.pop_front();
vis[u] = false;
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if(place[v] == place[u] && dis[v] > dis[u] + 1) {
dis[v] = dis[u] + 1;
dist[v] = dist[u] + edge[i].w;
if(! vis[v]) {
vis[v] = true;
int x = q.front();
if(!q.empty() && (dis[v] < dis[x] || dis[v] == dis[x] && dist[v] < dist[x]))
q.push_front(v);
else q.push_back(v);
}
}
else if(place[v] != place[u] && dis[v] > dis[u]) {
dis[v] = dis[u];
dist[v] = dist[u] + edge[i].w;
if(! vis[v]) {
vis[v] = true;
int x = q.front();
if(!q.empty() && (dis[v] < dis[x] || dis[v] == dis[x] && dist[v] < dist[x]))
q.push_front(v);
else q.push_back(v);
}
}
else if(place[v] == place[u] && dis[v] == dis[u] + 1 && dist[v] > dist[u] + edge[i].w) {
dist[v] = dist[u] + edge[i].w;
if(! vis[v]) {
vis[v] = true;
int x = q.front();
if(!q.empty() && (dis[v] < dis[x] || dis[v] == dis[x] && dist[v] < dist[x]))
q.push_front(v);
else q.push_back(v);
}
}
else if(place[v] != place[u] && dis[v] == dis[u] && dist[v] > dist[u] + edge[i].w) {
dist[v] = dist[u] + edge[i].w;
if(! vis[v]) {
vis[v] = true;
int x = q.front();
if(!q.empty() && (dis[v] < dis[x] || dis[v] == dis[x] && dist[v] < dist[x]))
q.push_front(v);
else q.push_back(v);
}
}
}
}
}
int main() {
freopen("festival.in", "r", stdin);
freopen("festival.out", "w", stdout);
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; i ++) {
int u, v, k;
scanf("%d %d %d", &v, &u, &k);
add(u, v, k);
}
memset(place, 0, sizeof(place));
for(int i = 1; i <= n; i ++)
if(! place[i]) tarjan(i);
spfa();
for(int i = 2; i <= n; i ++) {
if(dist[i] == dist[n + 1]) {
printf("-1\n");
continue;
}
printf("%I64d %I64d\n", dis[i], dist[i]);
}
return 0;
}