题意:n个节点的有向图,无重边,无自环。
对于一条从s->t的路径,除了t其他城市都可以被游览多次。理想路径是一条路径从s到t的字典序最小路径。q次询问,每次给出一个
si,ti
。问从
si
到
ti
的字典序最小的路径的第k个节点是什么。
可能不存在从
si
到
ti
的字典序最小路径,当且仅当有以下两种情况。
1.
没有从
si
到
ti
的路径。
2.
为了保证字典序最小的贪心走,结果在环路中陷入了死循环。
如果不考虑死循环的情况。
我们离线处理对每个询问,固定
si
,对领接表按序号sort之后,跑n次dfs即可。
如果考虑了死循环,第一次遍历环内的点,肯定都还是字典序最小的路径。而之后过环内点的路径都必然不是最小字典序路径了。
因此需要标记哪些点是环内点,以及是否是第一次访问,可以利用tarjan的low和dfn数组来完成这个事情。
第一次访问时,low都是INF,如果是环内点,那么之后low必然会被更新之前出现的一个dfn,所以如果low>dfn,则当前点必然是第一次被访问。
#define others
#ifdef poj
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
#endif // poj
#ifdef others
#include <bits/stdc++.h>
#endif // others
#define all(x) x.begin(), x.end()
using namespace std;
const double eps = 1e-8;
typedef long long LL;
namespace solver {
struct ASK {
int v, k, id;
};
const int maxn = 3300;
int n, m, q;
int ans[440000];
vector<int> G[maxn];
vector<ASK> ask[maxn];
int dfn[maxn], low[maxn], stk[maxn], vis[maxn], id, top;
vector<ASK> to[maxn];
void dfs(int u, int ok) {
dfn[u] = ++id;
vis[u] = 1;
stk[++top] = u;
if(ok) {
for(int i = 0; i < to[u].size(); i++)
ans[to[u][i].id] = stk[to[u][i].k];
}
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if(!dfn[v]) {
dfs(v, ok && low[u] > dfn[u]);
low[u] = min(low[u], low[v]);
} else if(vis[v])
low[u] = min(low[u], dfn[v]);
}
vis[u] = 0;
stk[top--] = 0;
}
void solve() {
scanf("%d%d%d", &n, &m, &q);
for(int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
}
for(int i = 1; i <= q; i++) {
int u, v, k;
scanf("%d%d%d", &u, &v, &k);
ask[u].push_back({v, k, i});
}
for(int i = 1; i <= n; i++)
sort(all(G[i]));
for(int i = 1; i <= n; i++) {
if(ask[i].size() == 0) continue;
memset(low, 0x3f3f3f3f, sizeof low);
memset(vis, 0, sizeof vis);
memset(dfn, 0, sizeof dfn);
for(int j = 1; j <= n; j++)
to[j].clear();
for(auto v:ask[i])
to[v.v].push_back(v);
top = id = 0;
dfs(i, 1);
}
for(int i = 1; i <= q; i++) {
if(ans[i] == 0) ans[i] = -1;
printf("%d\n", ans[i]);
}
}
}
int main() {
solver::solve();
return 0;
}