zoukankan      html  css  js  c++  java
  • 【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
    ");
    		else	printf("NIE
    ");
    	}
    	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 

     

  • 相关阅读:
    Address already in use: JVM_Bind 端口被占
    Excel PDF预览 excel导出
    js 判断日期是否节假日
    2020 idea的RunDashboard怎么显示出来
    sql server if else
    IDEA热部署总是失败的解决
    java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
    IOS开发 strong,weak,retain,assign,copy nomatic 等的区别与作用
    NSOperationQueue与GCD的使用原则和场景
    View加载过程
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7898644.html
Copyright © 2011-2022 走看看