zoukankan      html  css  js  c++  java
  • [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[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),表示一个询问。

    输出格式

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

    样例输入

    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

    样例输出

    TAK
    TAK
    TAK
    NIE

    解析

    如果全是加油站,问题就变成了对每一个询问,查找是否存在一条S到T的路径使该路径上的每一条边的长度均小于b。我们可以利用最小生成树中两点路径上的最大边最小的性质,对原图求最小生成树,利用倍增查询两点之间边权最大值,如果小于等于b就说明可行。

    但问题是还有其他的点。我们需要将原图改为只有加油站构成的图。可以发现,改编图中两点之间的一条边对应原图中两点的一条路径。设(p[i])表示离i最近的加油站,(dis[i])表示到最近的加油站的距离。观察一条的路径,一定会存在一条边使得该边的两端点的p值不同。也就是说,在求出(p)(dis)后,对于每一条边((u,v)),如果(p[u])不等于(p[v]),说明(p[u])(p[v])之间有一条路径,长度为(dis[u]+dis[v]+len(u,v))。对应地在新图中连边即可。

    还有几个需要注意的地方。

    1.这样做会导致重边很多,要注意去重。

    2.新图可能是不连通的,MST会是一个最小生成树森林。所以在倍增查询之前要判断两点是否在同一个连通块中。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #include <cmath>
    #define N 200002
    #define M 200002
    using namespace std;
    struct Edge{
    	int u,v,w;
    }e[M];
    int head[N],ver[M*2],nxt[M*2],edge[M*2],l;
    int n,s,m,q,i,j,c[N],p[N],dis[N],cnt,fa[N],dep[N],f[N][30],g[N][30],num,bcc[N];
    bool in[N];
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    void insert(int x,int y,int z)
    {
    	l++;
    	ver[l]=y;
    	edge[l]=z;
    	nxt[l]=head[x];
    	head[x]=l;
    }
    void SPFA()
    {
    	queue<int> q;
    	memset(dis,0x3f,sizeof(dis));
    	for(int i=1;i<=s;i++){
    		q.push(c[i]);
    		dis[c[i]]=0;in[c[i]]=1;
    		p[c[i]]=c[i];
    	}
    	while(!q.empty()){
    		int x=q.front();
    		q.pop();
    		for(int i=head[x];i;i=nxt[i]){
    			int y=ver[i];
    			if(dis[y]>dis[x]+edge[i]){
    				dis[y]=dis[x]+edge[i];
    				p[y]=p[x];
    				if(!in[y]){
    					in[y]=1;
    					q.push(y);
    				}
    			}
    		}
    		in[x]=0;
    	}
    }
    int my_comp(const Edge &x,const Edge &y)
    {
    	return x.w<y.w;
    }
    int find(int x)
    {
    	if(fa[x]!=x) fa[x]=find(fa[x]);
    	return fa[x];
    }
    void Kruskal()
    {
    	memset(head,0,sizeof(head));
    	l=0;
    	int num=n;
    	for(int i=1;i<=n;i++) fa[i]=i;
    	sort(e+1,e+cnt+1,my_comp);
    	for(int i=1;i<=cnt;i++){
    		if(num==1) break;
    		int f1=find(e[i].u),f2=find(e[i].v);
    		if(f1!=f2){
    			num--;
    			fa[f1]=f2;
    			insert(e[i].u,e[i].v,e[i].w);
    			insert(e[i].v,e[i].u,e[i].w);
    		}
    	}
    }
    void dfs(int x,int pre)
    {
    	bcc[x]=num;
    	dep[x]=dep[pre]+1;
    	f[x][0]=pre;
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y!=pre){
    			g[y][0]=edge[i];
    			dfs(y,x);
    		}
    	}
    }
    void init()
    {
    	for(int i=1;i<=s;i++){
    		if(!dep[c[i]]){
    			num++;
    			dfs(c[i],0);
    		}
    	}
    	for(int j=0;(1<<(j+1))<s;j++){
    		for(int i=1;i<=s;i++){
    			if(f[c[i]][j]==0) f[c[i]][j+1]=0;
    			else f[c[i]][j+1]=f[f[c[i]][j]][j];
    			g[c[i]][j+1]=max(g[c[i]][j],g[f[c[i]][j]][j]);
    		}
    	}
    }
    int ask(int u,int v)
    {
    	if(dep[u]>dep[v]) swap(u,v);
    	int tmp=dep[v]-dep[u],ans=0;
    	for(int i=0;(1<<i)<=tmp;i++){
    		if(tmp&(1<<i)){
    			ans=max(ans,g[v][i]);
    			v=f[v][i];
    		}
    	}
    	if(u==v) return ans;
    	for(int i=log2(1.0*s);i>=0;i--){
    		if(f[u][i]!=f[v][i]){
    			ans=max(ans,max(g[u][i],g[v][i]));
    			u=f[u][i],v=f[v][i];
    		}
    	}
    	ans=max(ans,max(g[u][0],g[v][0]));
    	return ans;
    }
    int main()
    {
    	n=read();s=read();m=read();
    	for(i=1;i<=s;i++) c[i]=read();
    	for(i=1;i<=m;i++){
    		int u=read(),v=read(),w=read();
    		insert(u,v,w);
    		insert(v,u,w);
    	}
    	SPFA();
    	for(i=1;i<=n;i++){
    		for(j=head[i];j;j=nxt[j]){
    			if(p[i]<p[ver[j]]) e[++cnt]=(Edge){p[i],p[ver[j]],dis[i]+dis[ver[j]]+edge[j]};
    		}
    	}
    	Kruskal();
    	init();
    	q=read();
    	for(i=1;i<=q;i++){
    		int x=read(),y=read(),b=read();
    		if(bcc[x]!=bcc[y]||ask(x,y)>b) puts("NIE");
    		else puts("TAK");
    	}
    	return 0;
    }
    

    总结

    • 这题提供了一个关于将原图转换为只有关键点的图的解法,需要积累。
    • 做题时可以将想到的东西写下来,将这些碎片式的思路组合成一个解法。
  • 相关阅读:
    20172305 2017-2018-2 《程序设计与数据结构》第十一周学习总结
    20172305 2017-2018-2 《程序设计与数据结构》实验四报告
    20172305 2017-2018-2 《程序设计与数据结构》实验三报告
    20172305 《程序设计与数据结构》第十周学习总结
    20172305 《程序设计与数据结构》第九周学习总结
    20172305 结对编程项目-四则运算 第二周 阶段总结
    20172305 《程序设计与数据结构》第八周学习总结
    20172305 结对编程项目-四则运算 第一周 阶段总结
    20172305 2017-2018-2 《程序设计与数据结构》实验二报告
    20172305 《程序设计与数据结构》第七周学习总结
  • 原文地址:https://www.cnblogs.com/LSlzf/p/11671002.html
Copyright © 2011-2022 走看看