zoukankan      html  css  js  c++  java
  • bzoj4144 [AMPPZ2014]Petrol

    Description

    给定一个 (n) 个点、 (m) 条边的带权无向图,其中有 (s) 个点是加油站。

    每辆车都有一个油量上限 (b) ,即每次行走距离不能超过 (b) ,但在加油站可以补满。

    (q) 次询问,每次给出 (x,y,b) ,表示出发点是 (x) ,终点是 (y) ,油量上限为 (b) ,且保证 (x) 点和 (y) 点都是加油站,请回答能否从 (x) 走到 (y)

    Input

    第一行包含三个正整数 (n,s,m(2le sle nle 200000,1le mle 200000)) ,表示点数、加油站数和边数。

    第二行包含 (s) 个互不相同的正整数 $c[1],c[2],cdots c[s] (1le c[i]le n) $ ,表示每个加油站。

    接下来 (m) 行,每行三个正整数 (u[i],v[i],d[i](1le u[i],v[i]le n,u[i] e v[i],1le d[i]le 10000)) ,表示 (u[i])(v[i]) 之间有一条长度为 (d[i]) 的双向边。

    接下来一行包含一个正整数 (q(1le qle 200000)) ,表示询问数。

    接下来 (q) 行,每行包含三个正整数 (x[i],y[i],b[i](1le x[i],y[i]le n,x[i] e y[i],1<le b[i]le 2 imes 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;
    }
    
  • 相关阅读:
    1-13Object类之toString方法
    jvm源码解读--16 锁_开头
    jvm源码解读--16 cas 用法解析
    jvm源码解读--15 oop对象详解
    jvm源码解读--14 defNewGeneration.cpp gc标记复制之后,进行空间清理
    jvm源码解读--13 gc_root中的栈中oop的mark 和copy 过程分析
    Error: Could not find or load main class ***
    使用javah 给.class类编译jni_helloworld.h文件头
    jvm源码解读--12 invokspecial指令的解读
    jvm源码解读--11 ldc指令的解读
  • 原文地址:https://www.cnblogs.com/aziint/p/8481500.html
Copyright © 2011-2022 走看看