bzoj4144 [AMPPZ2014]Petrol

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;
}

原文地址:https://www.cnblogs.com/aziint/p/8481500.html

时间: 2024-08-29 01:53:44

bzoj4144 [AMPPZ2014]Petrol的相关文章

bzoj4144 [AMPPZ2014]Petrol 图论 最短路 并查集

bzoj4144 [AMPPZ2014]Petrol 图论 最短路 并查集 1.这道题我们主要就是要求出距离一个油站的最近的油站 首先我们dijkstra 求出任意一个点到 离他最近的油站的距离 2.然后会发现 如果一条边的两个端点 的最近油站不同的话 那么这条边就会在这两个油站的最短路上 3.然后对于每一条边 我们将他的权值 变为 dis[ u ] + dis[ v ] + e[ i ][ j ] 如果u与v最近油站相同 那么这个无意义 如果不同 那么久表示 u 最近油站 到 v 最近油站的最

【BZOJ4144】[AMPPZ2014]Petrol 最短路+离线+最小生成树

[BZOJ4144][AMPPZ2014]Petrol Description 给定一个n个点.m条边的带权无向图,其中有s个点是加油站. 每辆车都有一个油量上限b,即每次行走距离不能超过b,但在加油站可以补满. q次询问,每次给出x,y,b,表示出发点是x,终点是y,油量上限为b,且保证x点和y点都是加油站,请回答能否从x走到y. Input 第一行包含三个正整数n,s,m(2<=s<=n<=200000,1<=m<=200000),表示点数.加油站数和边数. 第二行包含s

BZOJ 4144: [AMPPZ2014]Petrol

4144: [AMPPZ2014]Petrol Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 457  Solved: 170[Submit][Status][Discuss] Description 给定一个n个点.m条边的带权无向图,其中有s个点是加油站. 每辆车都有一个油量上限b,即每次行走距离不能超过b,但在加油站可以补满. q次询问,每次给出x,y,b,表示出发点是x,终点是y,油量上限为b,且保证x点和y点都是加油站,请回答能否从x走

4144: [AMPPZ2014]Petrol (多源最短路+最小生成树+启发式合并)

4144: [AMPPZ2014]Petrol Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 752  Solved: 298[Submit][Status][Discuss] Description 给定一个n个点.m条边的带权无向图,其中有s个点是加油站. 每辆车都有一个油量上限b,即每次行走距离不能超过b,但在加油站可以补满. q次询问,每次给出x,y,b,表示出发点是x,终点是y,油量上限为b,且保证x点和y点都是加油站,请回答能否从x走

[AMPPZ2014] Petrol

问题描述 给定一个n个点.m条边的带权无向图,其中有s个点是加油站. 每辆车都有一个油量上限b,即每次行走距离不能超过b,但在加油站可以补满. q次询问,每次给出x,y,b,表示出发点是x,终点是y,油量上限为b,且保证x点和y点都是加油站,请回答能否从x走到y. 输入格式 第一行包含三个正整数n,s,m(2<=s<=n<=200000,1<=m<=200000),表示点数.加油站数和边数. 第二行包含s个互不相同的正整数c[1],c[2],...c[s] (1<=c[

【BZOJ】【4144】【AMPPZ2014】Petrol

最短路+最小生成树+倍增 图论问题中综合性较强的一题= =(Orz vfk) 比较容易发现,关键的还是有加油站的这些点,其他点都是打酱油的. 也就是说我们重点是要求出 关键点之间的最短路. 这玩意……如果枚举加油站所在的点,然后跑单源最短路什么的……肯定TLE啊. 我们记from[i]表示离 i 最近的关键点,仔细考虑一下,A->B的最短路径上,一定是前一半的from[i]为A,然后走过某条路以后,后一半的from[i]为B.为什么呢?我们不妨设中间出现了一个点x的from[x]=C,那么我们大

AMPPZ2014

[AMPPZ2014]The Lawyer 记录每天结束的最早的会议以及开始的最晚的会议即可. #include<cstdio> #define N 500010 int n,m,i,d,a[N],b[N],st[21],en[21]; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&am

「AMPPZ2014」Petrol

传送门: 这是一道bzoj权限题 Luogu团队题链接 解题思路 首先对于每一个点 \(x\) 预处理出 \(nr[x]\) 和 \(dis[x]\),分别表示离 \(x\) 最近的加油站以及该段距离. 这个过程可以用多源 \(\text{Dijkstra}\) 处理. 然后对于每一条原图中的边 \((u, v, w)\)(\(nr[u]\ne nr[v]\)) 改为这样一条边:\((nr[u], nr[v], dis[u] + dis[v] + w)\) 然后只要离线用并查集维护一下连通性即可

【BZOJ 4148】 4148: [AMPPZ2014]Pillars (乱搞)

4148: [AMPPZ2014]Pillars Time Limit: 5 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 100  Solved: 49 Description 给定一个n*m的矩形,其中有f个2*2的障碍物,其中任意两个障碍物中心之间的欧几里得距离至少为6, 且每个障碍物的中心到边缘的距离至少为3.请找到一条从左下角(1,1)出发经过所有没有障碍物的点各 一次的且最后回到左下角的回路. Input 第一行包含三个整数n,