"Advanced Algorithm Contest Guide" # 0x61 Graph Theory-Shortest Path-Chapter1

Title link: https://www.acwing.com/activity/content/punch_the_clock/6/

There are many solutions to the shortest path:

BFS

Only applicable to graphs with an edge weight of 1.

DP

It is only applicable to the solution of DAG. When a point does not exist, then his answer can be determined immediately. This algorithm has no requirements other than DAG, and can run graphs with negative weights.

Dijkstra

Efficient and stable single-source shortest path algorithm, suitable for dealing with non-negative weight edges.

The complexity is: \ (O ((n + m) \ log (n + m)) \)

int n;
vector<pii> G[MAXN + 5];

bool vis[MAXN + 5];
int dis[MAXN + 5];
priority_queue<pii> PQ;

void Dijkstra(int s) {    
    while(!PQ.empty())
        PQ.pop();
    memset(vis, 0, sizeof(vis[0]) * (n + 1));
    memset(dis, INF, sizeof(dis[0]) * (n + 1));
    dis[s] = 0;
    PQ.push({-dis[s], s});
    while(!PQ.empty()) {
        int u = PQ.top().second;
        PQ.pop();
        if(vis[u])
            continue;
        vis[u] = 1;
        for(auto &e : G[u]) {
            int v = e.first, w = e.second;
            if(dis[u] + w < dis[v]) {
                dis[v] = dis[u] + w;
                PQ.push({-dis[v], v});
            }
        }
    }
    return;
}

Common techniques are:

Reverse drawing: The source point is the entire click, but the sink point is only a single, and the edge may be reversed at this time.

Hierarchical construction: specify certain edges to use some special properties, such as some magic can be used to reduce the weight of the edge, but the magic power is limited. The problem is that the range of magic power is generally not too large. It just happens that each point of the original image can be split into the range of magic power. Pay attention to extract the common nature of the edge to reduce the storage of the edge.

Multiple points are used as source points at the same time : it is different from the shortest path of multiple sources. Here, to find the shortest path from the source point set to each point, you can build a super source point, and then connect the source point set to the edge with a weight of 0, or directly Throw the entire point set into PQ.

Single source single sink: Similar to two-way BFS, it will be faster when exiting. Or when the meeting point is pushed out of the PQ by pushing, the exit can also be accelerated.

Double-ended BFS

Also known as 0-1BFS, the use of double-ended queues instead of queue BFS is suitable for graphs with edge weights of only 0 and 1, and can also be understood as the Dijkstra algorithm for manually controlling priority queues (in fact, there is no difference between these several).

The complexity is: \ (O (n + m) \)

vector<pii> G[MAXN + 5];

int dis[MAXN + 5];

deque<int> DQ;
void BFS(int s, int Limit) {
    DQ.clear();
    memset(dis, INF, sizeof(dis[0]) * (n + 1));
    dis[s] = 0;
    DQ.push_back(s);
    while(!DQ.empty()) {
        int u = DQ.front();
        DQ.pop_front();
        if(u == n)
            break;
        for(auto &e : G[u]) {
            int v = e.first, w = e.second;
            if(w == 0) {
                if(dis[u] < dis[v]) {
                    dis[v] = dis[u];
                    DQ.push_front(v);
                }
            } else {
                if(dis[u] + 1 < dis[v]) {
                    dis[v] = dis[u] + 1;
                    DQ.push_back(v);
                }
            }
        }
    }
    return;
}

skill:

It may be possible to convert some problems to 0-1BFS to solve, for example, the following 340 is to find the maximum value + layered construction, because the maximum value can be dichotomized, and then according to the relationship between the maximum value to distinguish whether the edge weight is 0 or 1 At the same time, the layered construction is the process of BFS.

SPFA

Also known as "Bellman-Ford algorithm for queue optimization", the complexity is very false, and the worst can be degraded to \ (O (nm) \) . It is applicable to any graph. If there is no negative ring that can be reached from the source point, SPFA can calculate the single-source shortest path, otherwise it will report the existence of a negative ring after the same node enqueues more than n times.

Floyd

The implementation is very simple. The shortest path between any two points can also correctly detect the negative loop. (After Floyd ends, there is a point where the shortest path from oneself to oneself is a negative number, so it can be circled indefinitely) The disadvantage is that the complexity is too large. Only suitable for very small pictures.

340. Communication line

Topic: There are N communication base stations in the suburbs, P bidirectional cables, and the i-th cable connects the base stations Ai and Bi. In particular, base station No. 1 is the main station of the communication company, and base station No. N is located in a farm. Now, the farmer wants to upgrade the communication line, in which Li cable is needed to upgrade the ith cable. The telephone company is holding discounts. The farmer can designate a path from base station 1 to base station N, and specify no more than K cables along the path, and the telephone company will provide free upgrade service. The farmer only needs to pay the cost of upgrading the most expensive cable among the remaining cables on the path. Ask for at least how much money to complete the upgrade.

data range:

0≤K<N≤1000,
1≤P≤10000,
1≤Li≤1000000

answer:

Hierarchical Dijkstra

From special to general, first consider the case of K = 0. At this time, it is to find a shortest path, but it is not an addition operation but a max operation. It can be directly solved by Dijkstra algorithm. Note that it seems that I have done this kind of questions before (it seems that the first time I saw it in 2018) is considered from the layered diagram. Looking at the data range, you can indeed build a layered graph.

Split a point into K + 1 points, the binary (id, k) indicates that the point with id has been used k times free, then the original edge is also split into super multiple edges, but it is very easy to pay attention to Up to these edges can be highly compressed.

The complexity is \ (O (n * k * \ log (n * k)) \) .

vector writing:

const int MAXNK = 1000 * 1001;

int n, p, k;
vector<pii> G[MAXNK + 5];

int id_ki_pos(int id, int ki) {
    return (id - 1) * (k + 1) + ki + 1;
}

int pos_id(int pos) {
    return (pos - 1) / (k + 1) + 1;
}

int pos_ki(int pos) {
    return (pos - 1) % (k + 1);
}

bool vis[MAXNK + 5];
int dis[MAXNK + 5];
priority_queue<pii> PQ;

void Dijkstra(int sid) {
    while(!PQ.empty())
        PQ.pop();
    memset(vis, 0, sizeof(vis[0]) * (n + 1) * (k + 1));
    memset(dis, INF, sizeof(dis[0]) * (n + 1) * (k + 1));
    int spos = id_ki_pos(sid, 0);
    dis[spos] = 0;
    PQ.push({-dis[spos], spos});
    while(!PQ.empty()) {
        int upos = PQ.top().second;
        PQ.pop();
        if(vis[upos])
            continue;
        vis[upos] = 1;
        int uid = pos_id(upos);
        int uki = pos_ki(upos);
        if(uid == n)
            break;
        for(auto &e : G[uid]) {
            int vid = e.first, w = e.second;
            {
                int v0pos = id_ki_pos(vid, uki);
                if(max(dis[upos], w) < dis[v0pos]) {
                    dis[v0pos] = max(dis[upos], w);
                    PQ.push({-dis[v0pos], v0pos});
                }
            }
            if(uki < k) {
                int v1pos = id_ki_pos(vid, uki + 1);
                if(max(dis[upos], 0) < dis[v1pos]) {
                    dis[v1pos] = max(dis[upos], 0);
                    PQ.push({-dis[v1pos], v1pos});
                }
            }
        }
    }
    return;
}

void TestCase() {
    scanf("%d%d%d", &n, &p, &k);
    for(int i = 1; i <= n; ++i)
        G[i].clear();
    for(int i = 1; i <= p; ++i) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        G[u].push_back({v, w});
        G[v].push_back({u, w});
    }
    Dijkstra(1);
    int ans = INF;
    for(int ki = 0; ki <= k; ++ki)
        ans = min(ans, dis[id_ki_pos(n, ki)]);
    if(ans == INF)
        ans = -1;
    printf("%d\n", ans);
    return;
}

Chained forward star writing:

const int MAXNK = 1000 * 1001;
const int MAXP = 1000000;

int n, p, k;

int G[MAXNK + 5];
struct Edge {
    int v, w, nxt;
    Edge() {}
    Edge(int v, int w, int nxt): v(v), w(w), nxt(nxt) {}
} edge[MAXP * 2 + 5];
int top;

void Init() {
    top = 0;
    memset(G, -1, sizeof(G[0]) * (n + 1));
}

void AddEdge(int u, int v, int w) {
    ++top;
    edge[top] = Edge(v, w, G[u]);
    G[u] = top;
}

int id_ki_pos(int id, int ki) {
    return (id - 1) * (k + 1) + ki + 1;
}

int pos_id(int pos) {
    return (pos - 1) / (k + 1) + 1;
}

int pos_ki(int pos) {
    return (pos - 1) % (k + 1);
}

bool vis[MAXNK + 5];
int dis[MAXNK + 5];
priority_queue<pii> PQ;

void Dijkstra(int sid) {
    while(!PQ.empty())
        PQ.pop();
    memset(vis, 0, sizeof(vis[0]) * (n + 1) * (k + 1));
    memset(dis, INF, sizeof(dis[0]) * (n + 1) * (k + 1));
    int spos = id_ki_pos(sid, 0);
    dis[spos] = 0;
    PQ.push({-dis[spos], spos});
    while(!PQ.empty()) {
        int upos = PQ.top().second;
        PQ.pop();
        if(vis[upos])
            continue;
        vis[upos] = 1;
        int uid = pos_id(upos);
        int uki = pos_ki(upos);
        if(uid == n)
            break;
        for(int eid = G[uid]; eid != -1; eid = edge[eid].nxt) {
            int vid = edge[eid].v, w = edge[eid].w;
            {
                int v0pos = id_ki_pos(vid, uki);
                if(max(dis[upos], w) < dis[v0pos]) {
                    dis[v0pos] = max(dis[upos], w);
                    PQ.push({-dis[v0pos], v0pos});
                }
            }
            if(uki < k) {
                int v1pos = id_ki_pos(vid, uki + 1);
                if(max(dis[upos], 0) < dis[v1pos]) {
                    dis[v1pos] = max(dis[upos], 0);
                    PQ.push({-dis[v1pos], v1pos});
                }
            }
        }
    }
    return;
}

void TestCase() {
    scanf("%d%d%d", &n, &p, &k);
    Init();
    for(int i = 1; i <= p; ++i) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        AddEdge(u, v, w);
        AddEdge(v, u, w);
    }
    Dijkstra(1);
    int ans = INF;
    for(int ki = 0; ki <= k; ++ki)
        ans = min(ans, dis[id_ki_pos(n, ki)]);
    if(ans == INF)
        ans = -1;
    printf("%d\n", ans);
    return;
}

reward:

  1. These subscripts are more annoying, you must draw them clearly on the paper.
  2. The number of edges is relatively large, and the number of layers is also relatively large. It is easy to get space when all the edges are added. I see that the writing of all the edges is simply nonsense.
  3. Chained forward star speed is significantly faster than vector, about 6 times faster, it is most likely that O2 optimization is not turned on.

Two points + double-ended BFS

The problem is to minimize the maximum value, which is a common dichotomy + verification problem. If the dichotomy enumerates a maximum value maxw, only need to record all edges <= maxw as cost = 0 (do not use free times), And record the side of> maxw as cost = 1 (use 1 free times), which becomes a classic 0-1BFS problem, which is solved with a double-ended queue. Cost = 1 is added from the tail of the team, and cost = 0 is taken from the head Join.

It is proved that this algorithm is the fastest and very space-saving, because the double-ended BFS constant is extremely small, and the verification speed is extremely fast (verification speed \ (O (n) \) ).

The complexity is \ (O (n * \ log MAXL) \) .

const int MAXN = 1000;
const int MAXP = 1000000;

int n, p, k;

int G[MAXN + 5];
struct Edge {
    int v, w, nxt;
    Edge() {}
    Edge(int v, int w, int nxt): v(v), w(w), nxt(nxt) {}
} edge[MAXP * 2 + 5];
int top;

void Init() {
    top = 0;
    memset(G, -1, sizeof(G[0]) * (n + 1));
}

void AddEdge(int u, int v, int w) {
    ++top;
    edge[top] = Edge(v, w, G[u]);
    G[u] = top;
}

int dis[MAXN + 5];

deque<int> DQ;
bool BFS(int s, int Limit) {
    DQ.clear();
    memset(dis, INF, sizeof(dis[0]) * (n + 1));
    dis[s] = 0;
    DQ.push_back(s);
    while(!DQ.empty()) {
        int u = DQ.front();
        DQ.pop_front();
        if(u == n)
            break;
        for(int eid = G[u]; eid != -1; eid = edge[eid].nxt) {
            int v = edge[eid].v, w = edge[eid].w;
            if(w <= Limit) {
                if(dis[u] < dis[v]) {
                    dis[v] = dis[u];
                    DQ.push_front(v);
                }
            } else {
                if(dis[u] + 1 < dis[v]) {
                    dis[v] = dis[u] + 1;
                    DQ.push_back(v);
                }
            }
        }
    }
    return dis[n] <= k;
}

void TestCase() {
    scanf("%d%d%d", &n, &p, &k);
    Init();
    for(int i = 1; i <= p; ++i) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        AddEdge(u, v, w);
        AddEdge(v, u, w);
    }
    int L = 0, R = INF, ans;
    while(1) {
        int M = (L + R) >> 1;
        if(L == M) {
            if(BFS(1, L)) {
                ans = L;
                break;
            }
            ans = R;
            break;
        }
        if(BFS(1, M))
            R = M;
        else
            L = M + 1;
    }
    if(ans == INF)
        ans = -1;
    printf("%d\n", ans);
    return;
}

reward:

  1. The problem of minimizing the maximum value can be converted into binary enumeration + quick verification.
  2. Make no mistake about the dichotomous value range, here starts from 0.
  3. The initial R is infinity at the dichotomy, and reaching infinity at the dichotomy indicates no solution.
  4. BFS does not require a vis array.

341. Best trade

Title: C has n large cities and m roads, each road connects to two of these n cities. There is at most one road directly connected between any two cities. Some of the m roads are one-way roads, and some are two-way roads. The two-way roads are counted as one when counting the number of roads. Country C has a vast territory, and the distribution of resources varies from place to place. This results in the price of the same commodity in different cities not necessarily the same. However, the buying price and selling price of the same commodity in the same city are always the same. Merchant Along came to travel in country C. When he learned the message "the price of the same product may be different in different cities", he decided to use the difference between the products in different cities to earn a little travel expenses while traveling. Suppose that the labels of the n cities in country C are from 1 to n, Along decided to start from city 1, and finally ended his trip in city n. In the process of tourism, any city can be repeated many times, but it is not required to pass through all n cities. Along earns travel expenses through this trade method: he will choose a passing city to buy his favorite commodity-a crystal ball, and then sell the crystal ball in another city after passing, using the earned difference as the Travel expenses. Because Along mainly traveled to country C, he decided that this trade would only be carried out at most once. Of course, he would not need to trade if he could not make the difference. Now give the crystal ball prices of n cities, and the information of m roads (the numbers of the two cities connected to each road and the traffic of the road).

Please tell Aaron how much travel he can earn.

data range:

1≤n≤100000, 1≤m≤500000,
1≤Crystal
ball price in various cities≤100

Solution: Because each city can pass through repeatedly, the problem becomes this: start from city 1, arrive at city x, buy a crystal ball; start from city x, arrive at city y, sell a crystal ball; Depart from city y and arrive at city n. Looking at the data, the price of the crystal ball seems to be a breakthrough?

Pseudo-algorithm: This problem has a very obvious solution for strong connected components. First, shrink all strong connected components, record the maximum and minimum value of each new point, and then turn into a DAG. Then find the minimum value (Dijkstra) on the path to the other point from the point 1 in the new figure that contains the original figure, and then find the other point to the meeting point from the point n that contains the original figure in the new figure. The maximum value on the path (Dijkstra on the reverse graph), and then take the maximum value for the traversal difference.

const int MAXN = 1e5;

int n, m;
int price[MAXN + 5];

namespace SCC {
    int n;
    vector<int> G[MAXN + 5], BG[MAXN + 5];

    int c1[MAXN + 5], cntc1;
    int c2[MAXN + 5], cntc2;
    int s[MAXN + 5], cnts;

    int n2;
    vector<int> V2[MAXN + 5];
    vector<int> G2[MAXN + 5], BG2[MAXN + 5];

    void Init(int _n) {
        n = _n;
        cntc1 = 0, cntc2 = 0, cnts = 0;
        for(int i = 1; i <= n; ++i) {
            G[i].clear();
            BG[i].clear();
            c1[i] = 0;
            c2[i] = 0;
            s[i] = 0;
            V2[i].clear();
            G2[i].clear();
            BG2[i].clear();
        }
        return;
    }

    void AddEdge(int u, int v) {
        G[u].push_back(v);
        BG[v].push_back(u);
        return;
    }

    void dfs1(int u) {
        c1[u] = cntc1;
        for(auto &v : G[u]) {
            if(!c1[v])
                dfs1(v);
        }
        s[++cnts] = u;
    }

    void dfs2(int u) {
        V2[cntc2].push_back(u);
        c2[u] = cntc2;
        for(auto &v : BG[u]) {
            if(!c2[v])
                dfs2(v);
        }
        return;
    }

    void Kosaraju() {
        for(int i = 1; i <= n; ++i) {
            if(!c1[i]) {
                ++cntc1;
                dfs1(i);
            }
        }
        for(int i = n; i >= 1; --i) {
            if(!c2[s[i]]) {
                ++cntc2;
                dfs2(s[i]);
            }
        }
        return;
    }

    void Build() {
        n2 = cntc2;
        for(int i = 1; i <= n2; ++i) {
            for(auto &u : V2[i]) {
                for(auto &v : G[u]) {
                    if(c2[v] != i) {
                        G2[i].push_back(c2[v]);
                        BG2[c2[v]].push_back(i);
                    }
                }
            }
        }
        for(int i = 1; i <= n2; ++i) {
            sort(G2[i].begin(), G2[i].end());
            G2[i].erase(unique(G2[i].begin(), G2[i].end()), G2[i].end());
            sort(BG2[i].begin(), BG2[i].end());
            BG2[i].erase(unique(BG2[i].begin(), BG2[i].end()), BG2[i].end());
        }
        return;
    }

    int minV2[MAXN + 5], maxV2[MAXN + 5];

    bool vis[MAXN + 5];
    priority_queue<pii> PQ;

    int mindis[MAXN + 5];

    void DijkstraMin(int s) {
        while(!PQ.empty())
            PQ.pop();
        memset(vis, 0, sizeof(vis[0]) * (n2 + 1));
        memset(mindis, INF, sizeof(mindis[0]) * (n2 + 1));
        mindis[s] = minV2[s];
        PQ.push({-mindis[s], s});
        while(!PQ.empty()) {
            int u = PQ.top().second;
            PQ.pop();
            if(vis[u])
                continue;
            vis[u] = 1;
            for(auto &v : G2[u]) {
                if(mindis[u] < mindis[v]) {
                    mindis[v] = min(minV2[v], mindis[u]);
                    PQ.push({-mindis[v], v});
                }
            }
        }
        return;
    }

    int maxdis[MAXN + 5];

    void DijkstraMax(int s) {
        while(!PQ.empty())
            PQ.pop();
        memset(vis, 0, sizeof(vis[0]) * (n2 + 1));
        memset(maxdis, -INF, sizeof(maxdis[0]) * (n2 + 1));
        maxdis[s] = maxV2[s];
        PQ.push({maxdis[s], s});
        while(!PQ.empty()) {
            int u = PQ.top().second;
            PQ.pop();
            if(vis[u])
                continue;
            vis[u] = 1;
            for(auto &v : BG2[u]) {
                if(maxdis[u] > maxdis[v]) {
                    maxdis[v] = max(maxV2[v], maxdis[u]);
                    PQ.push({maxdis[v], v});
                }
            }
        }
        return;
    }

    void Solve() {
        for(int i = 1; i <= n2; ++i) {
            minV2[i] = INF, maxV2[i] = -INF;
            for(auto &u : V2[i]) {
                minV2[i] = min(minV2[i], price[u]);
                maxV2[i] = max(maxV2[i], price[u]);
            }
        }
        DijkstraMin(c2[1]);
        DijkstraMax(c2[n]);
        int ans = 0;
        for(int i = 1; i <= n2; ++i)
            ans = max(ans, maxdis[i] - mindis[i]);
        printf("%d\n", ans);
        return;
    }
}

void TestCase() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &price[i]);
    SCC::Init(n);
    for(int i = 1; i <= m; ++i) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        SCC::AddEdge(u, v);
        if(w == 2)
            SCC::AddEdge(v, u);
    }
    SCC::Kosaraju();
    SCC::Build();
    SCC::Solve();
    return;
}

Note that the point weight Dijkstra must also be initialized to infinity (instead of the corresponding point weight), and then at the vis node (either when entering PQ, or when exiting PQ, and updating theoretically when entering PQ will be faster. ) Only when the corresponding point weight is updated. Because the meaning of dis [u] is "the minimum value of the point weights of all points passing from point s to point u".

Will the above method be a fake one? It seems to be quite fake. The group of friends also said that DAG had problems running Dijkstra? I thought of a counterexample:

5 5
5 4 2 1 3
1 2 1
1 3 1
2 4 1
4 3 1
3 5 1

This is exactly the same picture after the contraction point, Dijkstra's process is like this:

Take out point 1, add {2, 3} and {4, 2} to PQ;
take out point 3, add {2, 5} to PQ;
take out point 5, update the minimum value of the error to 2 . (Because 1-> 2-> 4-> 5 is the minimum value 1, the minimum value is obtained at point 4).

But although Dijkstra ran out of wrong results, my fake algorithm is too robust.

Construct a graph like this:

The tool for generating good-looking graphs is: https://csacademy.com/app/graph_editor/

In this figure, calculating the minimum price starting from 40, Dijkstra will directly mark 50 as 40 instead of the correct 10. In turn, calculating the maximum price to reach 50 will mark 10 as 20 instead of the correct 90. The reason is that Dijkstra is deceived by the local optimal situation.

The corresponding data is:

7 8
40 100 10 20 90 15 50
1 2
1 7
2 3
3 4
3 5
4 7
5 6
6 7

But it still seems to be stuck, and this fake algorithm is too robust, because he will even update the nodes that have been visited! In other words, Dijkstra would be more robust to remove the skipped vis node? But this algorithm is indeed wrong, only need to ensure that the successor node has been vised before the correct answer updates its successor node, then the new information of the successor node will not continue to be passed to its successor node.

However, DP is certainly not wrong after the contraction point. When a point has no entry, then it is impossible for him to modify it, and his answer is also determined.

const int MAXN = 1e5;

int n, m;
int price[MAXN + 5];

namespace SCC {
    int n;
    vector<int> G[MAXN + 5], BG[MAXN + 5];

    int c1[MAXN + 5], cntc1;
    int c2[MAXN + 5], cntc2;
    int s[MAXN + 5], cnts;

    int n2;
    vector<int> V2[MAXN + 5];
    vector<int> G2[MAXN + 5], BG2[MAXN + 5];

    void Init(int _n) {
        n = _n;
        cntc1 = 0, cntc2 = 0, cnts = 0;
        for(int i = 1; i <= n; ++i) {
            G[i].clear();
            BG[i].clear();
            c1[i] = 0;
            c2[i] = 0;
            s[i] = 0;
            V2[i].clear();
            G2[i].clear();
            BG2[i].clear();
        }
        return;
    }

    void AddEdge(int u, int v) {
        G[u].push_back(v);
        BG[v].push_back(u);
        return;
    }

    void dfs1(int u) {
        c1[u] = cntc1;
        for(auto &v : G[u]) {
            if(!c1[v])
                dfs1(v);
        }
        s[++cnts] = u;
    }

    void dfs2(int u) {
        V2[cntc2].push_back(u);
        c2[u] = cntc2;
        for(auto &v : BG[u]) {
            if(!c2[v])
                dfs2(v);
        }
        return;
    }

    void Kosaraju() {
        for(int i = 1; i <= n; ++i) {
            if(!c1[i]) {
                ++cntc1;
                dfs1(i);
            }
        }
        for(int i = n; i >= 1; --i) {
            if(!c2[s[i]]) {
                ++cntc2;
                dfs2(s[i]);
            }
        }
        return;
    }

    void Build() {
        n2 = cntc2;
        for(int i = 1; i <= n2; ++i) {
            for(auto &u : V2[i]) {
                for(auto &v : G[u]) {
                    if(c2[v] != i) {
                        G2[i].push_back(c2[v]);
                        BG2[c2[v]].push_back(i);
                    }
                }
            }
        }
        for(int i = 1; i <= n2; ++i) {
            sort(G2[i].begin(), G2[i].end());
            G2[i].erase(unique(G2[i].begin(), G2[i].end()), G2[i].end());
            sort(BG2[i].begin(), BG2[i].end());
            BG2[i].erase(unique(BG2[i].begin(), BG2[i].end()), BG2[i].end());
        }
        return;
    }

    int minV2[MAXN + 5], maxV2[MAXN + 5];
    int indeg[MAXN + 5], outdeg[MAXN + 5];
    int vis1[MAXN + 5], visn[MAXN + 5];
    queue<int> Q;

    void DPin() {
        while(!Q.empty())
            Q.pop();
        for(int i = 1; i <= n2; ++i) {
            vis1[i] = 0;
            indeg[i] = BG2[i].size();
            if(indeg[i] == 0)
                Q.push(i);
        }
        while(!Q.empty()) {
            int u = Q.front();
            Q.pop();
            if(c2[1] == u)
                vis1[u] = 1;
            for(auto &v : G2[u]) {
                --indeg[v];
                if(indeg[v] == 0)
                    Q.push(v);
                if(vis1[u] == 1) {
                    minV2[v] = min(minV2[v], minV2[u]);
                    vis1[v] = 1;
                }
            }
        }
    }

    void DPout() {
        while(!Q.empty())
            Q.pop();
        for(int i = 1; i <= n2; ++i) {
            visn[i] = 0;
            outdeg[i] = G2[i].size();
            if(outdeg[i] == 0)
                Q.push(i);
        }
        while(!Q.empty()) {
            int u = Q.front();
            Q.pop();
            if(c2[n] == u)
                visn[u] = 1;
            for(auto &v : BG2[u]) {
                --outdeg[v];
                if(outdeg[v] == 0)
                    Q.push(v);
                if(visn[u] == 1) {
                    maxV2[v] = max(maxV2[v], maxV2[u]);
                    visn[v] = 1;
                }
            }
        }
    }

    void Solve() {
        for(int i = 1; i <= n2; ++i) {
            minV2[i] = INF, maxV2[i] = -INF;
            for(auto &u : V2[i]) {
                minV2[i] = min(minV2[i], price[u]);
                maxV2[i] = max(maxV2[i], price[u]);
            }
            indeg[i] = BG2[i].size();
            outdeg[i] = G2[i].size();
        }
        DPin();
        DPout();
        int ans = 0;
        for(int i = 1; i <= n2; ++i) {
            if(vis1[i] == 1 && visn[i] == 1)
                ans = max(ans, maxV2[i] - minV2[i]);
        }
        printf("%d\n", ans);
        return;
    }
}

void TestCase() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &price[i]);
    SCC::Init(n);
    for(int i = 1; i <= m; ++i) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        SCC::AddEdge(u, v);
        if(w == 2)
            SCC::AddEdge(v, u);
    }
    SCC::Kosaraju();
    SCC::Build();
    SCC::Solve();
    return;
}

Or you can use SPFA directly for two times, after all, this fake algorithm is simple and brainless.


reward:

  1. The fake algorithm can even pass the question, if not, you can try a fake wave, if it is not against my fake algorithm card, it is very difficult to get stuck.
  2. There is no intentionally card SPFA, and SPFA will not be accepted until the official regional competition.

Guess you like

Origin www.cnblogs.com/KisekiPurin2019/p/12667294.html