ACwing342. 道路与航线

发布时间 2023-12-04 17:34:15作者: Gold_stein

这道题是把拓扑排序和迪杰斯特拉交叉进行。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
typedef pair<int, int> PII;

const int N = 25005, M = 50005, inf = 1e9;

int h[N], e[M * 3], w[M * 3], ne[M * 3], cnt, id[N], idnow;
int n, r, p, s;
bool flag[N], vis[N];
int dist[N], in_degree[N];
vector<int> vec[N];
queue<int> q_block;

inline void add(int a, int b, int c)
{
    e[++cnt] = b;
    w[cnt] = c;
    ne[cnt] = h[a];
    h[a] = cnt;
}

void dfs(int u,int mark_id)
{
    vec[mark_id].push_back(u);
    id[u] = mark_id;
    for(int i = h[u]; i; i = ne[i])
    {
        int j = e[i];
        if(!flag[j])
        {
            flag[j] = 1;
            dfs(j, mark_id);
        }
    }
}

void DJ(int u)
{
    priority_queue<PII, vector<PII>, greater<PII>> Q;
    for(auto i : vec[u])
        Q.push({dist[i], i});
    while(!Q.empty())
    {
        PII q_top = Q.top();
        Q.pop();
        int x = q_top.second;
        if(vis[x]) continue;
        vis[x] = 1;
        for(int i = h[x]; i; i = ne[i])
        {
            int j = e[i];
            if(id[j] == id[x])
            {
                if(dist[j] > dist[x] + w[i])
                {
                    dist[j] = dist[x] + w[i];
                    Q.push({dist[j], j});
                }
            }
            else
            {
                dist[j] = min(dist[j], dist[x] + w[i]);
                in_degree[id[j]]--;
                if(!in_degree[id[j]])
                {
                    q_block.push(id[j]);
                }
            }
        }
    }
}

void func()
{
    memset(dist, 0x3f, sizeof(dist));
    dist[s] = 0;
    for(int i = 1; i <= idnow; i++)
    {
        if(!in_degree[i])
        {
            q_block.push(i);
        }
    }
    while(!q_block.empty())
    {
        int block_first = q_block.front();
        q_block.pop();
        DJ(block_first);
    }
}

int main()
{
    scanf("%d%d%d%d", &n, &r, &p, &s);
    for(int i = 1; i <= r; i++)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
        add(b, a, c);
    }
    for(int i = 1; i <= n; i++)
        if(!flag[i])
        {
            flag[i] = 1;
            idnow++;
            dfs(i, idnow);
        }
    for(int i = 1; i <= p; i++)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
        in_degree[id[b]]++;
    }
    func();
    for(int i = 1; i <= n; i++)
        if(dist[i] < inf)
            printf("%d\n", dist[i]);
        else
            puts("NO PATH");
    system("pause");
    return 0;
}

为了方便处理,省去各种各样的特判(同时保持拓扑排序的正确性),我们在进入到一个新连通块时,在进行迪杰斯特拉之前,直接把这个连通块内的所有点统一加入优先队列。