1. 程式人生 > >P2245 星際導航 瓶頸路

P2245 星際導航 瓶頸路

printf text nds 不可達 無向圖 pos kruskal void 星際

\(\color{#0066ff}{ 題目描述 }\)

sideman 做好了回到 \(\text{Gliese}\) 星球的硬件準備,但是 \(\text{sideman}\) 的導航系統還沒有完全設計好。為了方便起見,我們可以認為宇宙是一張有$ N$ 個頂點和 \(M\) 條邊的帶權無向圖,頂點表示各個星系,兩個星系之間有邊就表示兩個星系之間可以直航,而邊權則是航行的危險程度。

\(\text{sideman}\) 現在想把危險程度降到最小,具體地來說,就是對於若幹個詢問 \((A, B)\)\(\text{sideman}\) 想知道從頂點 \(A\)航行到頂點 \(B\) 所經過的最危險的邊的危險程度值最小可能是多少。作為 \(\text{sideman}\)

的同學,你們要幫助 \(\text{sideman}\)返回家園,兼享受安全美妙的宇宙航行。所以這個任務就交給你了。

\(\color{#0066ff}{輸入格式}\)

第一行包含兩個正整數 \(N\)\(M\),表示點數和邊數。

之後 \(M\) 行,每行三個整數 \(A\)\(B\)\(L\),表示頂點 \(A\)\(B\) 之間有一條邊長為 \(L\) 的邊。頂點從 \(1\) 開始標號。

下面一行包含一個正整數 \(Q\),表示詢問的數目。

之後 \(Q\) 行,每行兩個整數 \(A\) 和 BB,表示詢問 \(A\)\(B\) 之間最危險的邊危險程度的可能最小值。

\(\color{#0066ff}{輸出格式}\)

對於每個詢問, 在單獨的一行內輸出結果。如果兩個頂點之間不可達, 輸出 \(\text{impossible}\)

\(\color{#0066ff}{輸入樣例}\)

4 5
1 2 5
1 3 2
2 3 11
2 4 6
3 4 4
3
2 3
1 4
1 2

\(\color{#0066ff}{輸出樣例}\)

5
4
5

\(\color{#0066ff}{數據範圍與提示}\)

對於 \(40\%\) 的數據,滿足 \(N \leq 1000, M \leq 3000, Q \leq 1000\)

對於 \(80\%\) 的數據,滿足 \(N \leq 10000, M \leq 10^5, Q \leq 1000\)

對於 $100%$100% 的數據,滿足 \(N \leq 10^5, M \leq 3 \times 10^5, Q \leq 10^5, L \leq 10^9\)。數據不保證沒有重邊和自環。

\(\color{#0066ff}{ 題解 }\)

一個無向圖,看到最大邊最小,我去,最小生成樹。。。

然後神奇的發現,題目不保證沒有重邊自環的問題就解決了

於是我們就得到了一個森林

每次詢問兩個點,如果不在一棵樹中(通過最小生成樹的ufs判斷)輸出無解

否則輸出其路徑上最大邊邊權,顯然倍增搞一下就行了

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
int n, m;
const int maxn = 1e5 + 10;
struct node {
    int to, dis;
    node *nxt;
    node(int to = 0, int dis = 0, node *nxt = NULL): to(to), dis(dis), nxt(nxt) {}
    void *operator new(size_t) {
        static node *S = NULL, *T = NULL;
        return (S == T) && (T = (S = new node[1024]) + 1024), S++;
    }
}*head[maxn];
bool vis[maxn];
int dep[maxn];
struct E {
    int x, y, dis;
    friend bool operator < (const E &a, const E &b) {
        return a.dis < b.dis;
    }
}e[maxn << 2];
int f[maxn][25], max[maxn][25];
int b[maxn];
void add(int from, int to, int dis) {
    head[from] = new node(to, dis, head[from]);
}
int findset(int x) { return x == b[x]? b[x] : b[x] = findset(b[x]); }
void Kruskal() {
    for(int i = 1; i <= n; i++) b[i] = i;
    for(int i = 1; i <= m; i++) e[i].x = in(), e[i].y = in(), e[i].dis = in();
    std::sort(e + 1, e + m + 1);
    for(int i = 1; i <= m; i++) {
        int xx = findset(e[i].x);
        int yy = findset(e[i].y);
        if(xx != yy) {
            b[xx] = yy;
            add(e[i].x, e[i].y, e[i].dis);
            add(e[i].y, e[i].x, e[i].dis);
        }
    }
}
void dfs(int x, int fa) {
    vis[x] = true;
    dep[x] = dep[fa] + 1;
    f[x][0] = fa;
    for(node *i = head[x]; i; i = i->nxt) {
        if(i->to == fa) continue;
        max[i->to][0] = i->dis;
        dfs(i->to, x);
    }
}
void beizeng() {
    for(int i = 1; i <= n; i++) if(!vis[i]) dfs(i, 0);
    for(int j = 1; j <= 24; j++)
        for(int i = 1; i <= n; i++) {
            f[i][j] = f[f[i][j - 1]][j - 1];
            max[i][j] = std::max(max[i][j - 1], max[f[i][j - 1]][j - 1]);
        }
}
int LCA(int x, int y) {
    int dis = 0;
    if(dep[x] < dep[y]) std::swap(x, y);
    for(int i = 24; i >= 0; i--) 
        if(dep[f[x][i]] >= dep[y]) 
            dis = std::max(dis, max[x][i]), x = f[x][i];
    if(x == y) return dis;
    for(int i = 24; i >= 0; i--) 
        if(f[x][i] != f[y][i]) {
            dis = std::max(dis, std::max(max[x][i], max[y][i]));
            x = f[x][i], y = f[y][i];
        }
    return std::max(dis, std::max(max[x][0], max[y][0]));
}

void query() {
    int x, y;
    for(int T = in(); T --> 0;) {
        x = in(), y = in();
        if(findset(x) != findset(y)) printf("impossible\n");
        else printf("%d\n", LCA(x, y));
    }
}

int main() {
    n = in(), m = in();
    Kruskal();
    beizeng();
    query();
    return 0;
}

P2245 星際導航 瓶頸路