codeforces 864F 图论

题意: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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值