1. 程式人生 > >bzoj4144 [AMPPZ2014]Petrol

bzoj4144 [AMPPZ2014]Petrol

具體細節 include flag getchar() tor 黑點 strong time bool

Description

給定一個 \(n\)個 點、 \(m\) 條邊的帶權無向圖,其中有 \(s\) 個點是加油站。

每輛車都有一個油量上限 \(b\) ,即每次行走距離不能超過 \(b\) ,但在加油站可以補滿。

\(q\) 次詢問,每次給出 \(x,y,b\) ,表示出發點是 \(x\) ,終點是 \(y\) ,油量上限為 \(b\) ,且保證 \(x\) 點和 \(y\) 點都是加油站,請回答能否從 \(x\) 走到 \(y\)

Input

第一行包含三個正整數 \(n,s,m(2\le s\le n\le 200000,1\le m\le 200000)\) ,表示點數、加油站數和邊數。

第二行包含 \(s\)

個互不相同的正整數 $c[1],c[2],\cdots cs $ ,表示每個加油站。

接下來 \(m\) 行,每行三個正整數 \(u[i],v[i],d[i](1\le u[i],v[i]\le n,u[i]\ne v[i],1\le d[i]\le 10000)\) ,表示 \(u[i]\)\(v[i]\) 之間有一條長度為 \(d[i]\) 的雙向邊。

接下來一行包含一個正整數 \(q(1\le q\le 200000)\) ,表示詢問數。

接下來 \(q\) 行,每行包含三個正整數 \(x[i],y[i],b[i](1\le x[i],y[i]\le n,x[i]\ne y[i],1<\le b[i]\le 2\times 10^9)\)

,表示一個詢問。

Output

輸出 \(q\) 行。第 \(i\) 行輸出第i個詢問的答案,如果可行,則輸出 \(\mathrm{TAK}\) ,否則輸出 \(\mathrm{NIE}\)

Sample

Sample Input

6 4 5
1 5 2 6
1 3 1
2 3 2
3 4 3
4 5 5
6 4 5
4
1 2 4
2 6 9
1 5 9
6 5 8

Sample Output

TAK
TAK
TAK
NIE

Solution

真是一道結論好題。

大家肯定知道,不是加油站的點是廢點。那加油站點該怎麽重新建圖呢?

來看一個圖。紅點表示加油站,黑點是廢點。

技術分享圖片

\(1\)

\(3\) 走簡單路徑會至少需要 \(7\) 的油量,而從 \(1\)\(4\) 再到 \(3\) 則只需要準備 \(5\) 的油量就可以了。這是因為 \[c<a 且 c<b \] 所以 \[c+b<a+b 且c+a<b+a\] 於是我們就得到了結論

  • 從當前節點到最近的加油站再到其它的加油站不會更差

那麽就可以多源最短路,最小生成樹判斷連通性就可以了。

具體細節見代碼。

#include<bits/stdc++.h>
using namespace std;

#define N 400011
#define rep(i, a, b) for (int i = a; i <= b; i++)

inline int read() {
    int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
    while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}

int n, s, m, C[N], head[N], tot = 1, cnt, dis[N], near[N], fa[N];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
queue<int> q;
bool inq[N], ans[N];
struct edge { int v, w, next; }e[N];
inline void add(int u, int v, int w) { e[++tot].v = v, e[tot].w = w, e[tot].next = head[u], head[u] = tot; }
struct edgeData {
    int u, v, w;
    edgeData(int _u = 0, int _v = 0, int _w = 0):u(_u), v(_v), w(_w) {}
    bool operator < (const edgeData& b) const { return w < b.w; }
}edt[N];
struct query {
    int id, S, T, d;
    bool operator < (const query& b) const { return d < b.d; }
}qu[N];
void spfa() {
    rep(i, 1, n) dis[i] = 0x7fffffff;
    rep(i, 1, s) q.push(C[i]), inq[C[i]] = 1, dis[C[i]] = 0, near[C[i]] = C[i];
    while (!q.empty()) {
        int u = q.front(); q.pop(), inq[u] = 0;
        for (int i = head[u], v; i; i = e[i].next) if (dis[v = e[i].v] > dis[u] + e[i].w) {
            dis[v] = dis[u] + e[i].w, near[v] = near[u];
            if (!inq[v]) q.push(v), inq[v] = 1;
        }
    }
    rep(u, 1, n) for (int i = head[u], v; i; i = e[i].next) if (near[u] ^ near[v = e[i].v])
        edt[++cnt] = edgeData(near[u], near[v], dis[u] + dis[v] + e[i].w);
    sort(edt + 1, edt + 1 + cnt);
}
int main() {
    n = read(), s = read(), m = read();
    rep(i, 1, n) fa[i] = i;
    rep(i, 1, s) C[i] = read();
    rep(i, 1, m) { int u = read(), v = read(), w = read(); add(u, v, w), add(v, u, w); }
    spfa();
    int q = read();
    rep(i, 1, q) qu[i].S = read(), qu[i].T = read(), qu[i].d = read(), qu[i].id = i;
    sort(qu + 1, qu + 1 + q);
    int pos = 1;
    rep(i, 1, q) {
        while (pos <= cnt && edt[pos].w <= qu[i].d) {
            int x = find(edt[pos].u), y = find(edt[pos].v);
            if (x ^ y) fa[x] = y;
            pos++;
        }
        ans[qu[i].id] = (find(qu[i].S) == find(qu[i].T));
    }
    rep(i, 1, q) puts(ans[i] ? "TAK" : "NIE");
    return 0;
}

bzoj4144 [AMPPZ2014]Petrol