【tarjan思想 && 离线处理】Codeforces Round #436 (Div. 2) F - Cities Excursions

16 篇文章 0 订阅
11 篇文章 0 订阅

Problem Description

给你n个城市m条单向的道路,q个访问。
问你u城市到v城市字典序最小的路径的第k个城市。
不存在路径的情况:
u->v城市的过程中存在一个使字典序一直变小的环。
u根本无法到达v。

思路:

一开始想到肯定是dfs,但是不知道如何跑,看了一些大佬的博客才知道如何处理。
我们预处理一下,所得dfs所走的下一个点,一定是字典序最小的。
这里用到了tarjan算法的思想。我们在dfs的过程中,回到了祖先节点,代表这个环上的点有路径到达的其他点,全都不能到达,因为这个环会使得字典序不断变小。
然后就是如何判断该点是否是环(平时tarjan算法,单独的点也算一个环,这里的环是指两个点以上的环)上点,该点是否是可以到达的点。
具体看代码操作

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3005;
const int inf = 0x3f3f3f3f;
struct node
{
    int u, v, k, id;
    bool operator < (const node &b) const{
        if(u == b.u) return v < b.v;
        else return u < b.u;
    }
};
node a[400055];
vector<int> Map[maxn], pos[maxn];
int path[maxn], num, dfn[maxn], low[maxn], vis[maxn];
int ans[400055];
void dfs(int u, int step, int ok)
{
    dfn[u] = num++; low[u] = inf;//low[]初始化为inf很好,dfn[u]<low[u]就可以判断该点是否是环(这个环至少有两个点组成)上点。
    vis[u] = 1;//用来标记是否是祖先
    path[step] = u;//记录路径
    if(ok)//代表这个点可以到达
    {
        for(int i = 0; i < pos[u].size(); i++)
        {
            int to = pos[u][i];
            if(a[to].k <= step) ans[a[to].id] = path[a[to].k];
        }
    }
    for(int i = 0; i < Map[u].size(); i++)
    {
        int to = Map[u][i];
        if(!dfn[to])
        {
            dfs(to, step+1, ok && dfn[u]<low[u]);//当前点可以到达&&当前点不是环上的点。这样下一个点就是可以到达的
            low[u] = min(low[u], low[to]);
        }
        else if(vis[to]) low[u] = min(low[u], dfn[to]);//祖先点
    }
    vis[u] = 0;
}
void add(int u, int v)
{
    Map[u].push_back(v);
}
int main()
{
    int n, m, q, u, v, i;
    scanf("%d %d %d", &n, &m, &q);
    while(m--) scanf("%d %d", &u, &v), add(u, v);
    for(i = 1; i <= n; i++) sort(Map[i].begin(), Map[i].end());//预处理,这样dfs的是按照字典序走
    for(i = 0; i < q; i++)
    {
        scanf("%d %d %d", &a[i].u, &a[i].v, &a[i].k);
        a[i].id = i;
    }
    sort(a, a + q);//对于所有具有相同起始点的询问,都可以一起处理
    memset(ans, -1, sizeof(ans));
    int cnt = 0;
    for(i = 1; i <= n; i++)
    {
        if(a[cnt].u == i)
        {
            while(a[cnt].u == i && cnt < q)
            {
                pos[a[cnt].v].push_back(cnt);//记录到达v,询问下标
                cnt++;
            }
            num = 1;
            memset(dfn, 0, sizeof(dfn));
            memset(vis, 0, sizeof(vis));
            dfs(i, 1, 1);
        }
        for(int j = 1; j <= n; j++) pos[j].clear();
    }
    for(i = 0; i < q; i++)
        printf("%d\n", ans[i]);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值