【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个互不相同的正整数c[1],c[2],...c[s](1<=c[i]<=n),表示每个加油站。

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

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

接下来q行,每行包含三个正整数x[i],y[i],b[i](1<=x[i],y[i]<=n,x[i]!=y[i],1<=b[i]<=2*10^9),表示一个询问。

Output

输出q行。第i行输出第i个询问的答案,如果可行,则输出TAK,否则输出NIE。

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

题解:比较暴力的想法就是求出所有加油站之间的最短路,但显然复杂度太高,那么我们换一种思路,考虑每条边的贡献。

先跑多源最短路,求出对于每个点,离它最近的加油站是哪个,记为pre,以及最短路长度dis。然后枚举每条边<a,b>,如果pre[a]=pre[b],那么这条边显然没啥用。否则,我们在新图中连一条边<pre[a],pre[b]>,长度为dis[a]+dis[b]+val<a,b>。为什么可以这样做呢?因为假如我们从加油站c经过这条边想走到d,且c!=pre[a]&&c!=pre[b],那么dist(a,c)>dis[a],dist(b,c)>dis[b],我们可以先不走这条边,先去a和b加油,再回到这条边上,剩的油一定是不会比原来少的。

所以我们可以对于新图求最小生成树,并离线处理所有询问,将边从小到大扔到图中,用并查集判断两个点能否连通即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <utility>
#define mp(A,B) make_pair(A,B)
using namespace std;
const int maxn=200010;
int n,K,m,Q,cnt,tot,sum;
int pos[maxn],to[maxn<<1],next[maxn<<1],val[maxn<<1],head[maxn],dis[maxn],pre[maxn],inq[maxn],vis[maxn];
int f[maxn],ans[maxn];
priority_queue<pair<int,int> > pq;
struct node
{
	int a,b,v,org;
}p[maxn],q[maxn];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
bool cmp(const node &a,const node &b)
{
	return a.v<b.v;
}
int find(int x)
{
	return (f[x]==x)?x:(f[x]=find(f[x]));
}
inline void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
int main()
{
	n=rd(),K=rd(),m=rd();
	int i,j,u,a,b,c;
	memset(dis,0x3f,sizeof(dis)),memset(head,-1,sizeof(head));
	for(i=1;i<=K;i++)	a=pos[i]=rd(),dis[a]=0,pre[a]=i,pq.push(mp(0,a));
	for(i=1;i<=m;i++)	a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c);
	while(!pq.empty())
	{
		u=pq.top().second,pq.pop();
		if(vis[u])	continue;
		vis[u]=1;
		for(i=head[u];i!=-1;i=next[i])	if(dis[to[i]]>dis[u]+val[i])
			dis[to[i]]=dis[u]+val[i],pre[to[i]]=pre[u],pq.push(mp(-dis[to[i]],to[i]));
	}
	for(i=1;i<=n;i++)	for(j=head[i];j!=-1;j=next[j])	if(pre[i]<pre[to[j]])
		p[++tot].a=pre[i],p[tot].b=pre[to[j]],p[tot].v=dis[i]+dis[to[j]]+val[j];
	sort(p+1,p+tot+1,cmp);
	for(i=1;i<=n;i++)	f[i]=i;
	Q=rd();
	for(i=1;i<=Q;i++)	q[i].a=pre[rd()],q[i].b=pre[rd()],q[i].v=rd(),q[i].org=i;
	sort(q+1,q+Q+1,cmp);
	for(i=j=1;i<=Q;i++)
	{
		for(;j<=tot&&p[j].v<=q[i].v;j++)
		{
			a=find(p[j].a),b=find(p[j].b);
			if(a!=b)	f[a]=b;
		}
		if(find(q[i].a)==find(q[i].b))	ans[q[i].org]=1;
	}
	for(i=1;i<=Q;i++)
	{
		if(ans[i])	printf("TAK\n");
		else	printf("NIE\n");
	}
	return 0;
}//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 
时间: 2024-11-01 16:55:46

【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

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

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走

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走

Codeforces Round #303 (Div. 2) E. Paths and Trees (最短路+变形最小生成树)

题目地址:E. Paths and Trees 模拟了一场CF,这场实在太水了..边玩边做的..最后半分钟交了一发E题..不幸AK绝杀失败.... 首先的思路肯定是先求最短路,把可能为最短路的边挑出来,然后第二步我本来写的是直接用无向图的最小生成树,于是绝杀失败...后来才发现这样是不行的..因为边是有向边,而且每个点的入度要保证只有一个才行.于是我就把最小生成树的边弄成有向边,然后判定一下每个点的入度保证为1.然后就过了.. 代码如下: #include <iostream> #includ

区别 最短路跟最小生成树

首先是定义上 最小生成树能够保证整个拓扑图的所有路径之和最小,但不能保证任意两点之间是最短路径. 最短路径是从一点出发,到达目的地的路径最小. 实现方法 1. 最小生成树 最小生成树有两种算法来得到:Prims算法和Kruskal算法. Kruskal算法:根据边的加权值以递增的方式,一次找出加权值最低的边来构建最小生成树,而且规定:每次添加的边不能造成生成树有回路,知道找到N-1个边为止. Prims算法:以每次加入一个的临界边来建立最小生成树,直到找到N-1个边为止.其规则为:以开始时生成树

模板(最短路,最小生成树,并查集)

单源最短路 #include<queue> #include<cstdio> #define INF 2147483647LL using namespace std; struct node { int to,dis,next; }; struct node edge[500005]; int n,m,num,head[10001],dis_1[10001]; inline void edge_add(int from,int to,int dis) { num++; edge[

图论知识整理(2) 拓扑排序、最短路、最小生成树

===================================== 发现以前图论学的很不好,什么都不会,现在开始整理图论知识了 作者就是个蒟蒻,ORZ各位大神们 ===================================== 定义:对一个有向无环图(Directed Acyclic Graph,简称DAG)进行拓扑排序,将图中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若<u,v> ∈E(G),则u在线性序列中出现在v之前. 说的好像很有道理,然而我并没有看懂它在

[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[