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

Solution

$get$了一个新的小$trick$……话说为什么这么常用我之前竟然没有碰到过

多源最短路,初始把多个点扔到堆/队列里,最后可以求出来每个点到最近的源点的距离。

然后发现这个题,只有加油站那些点是有用的。然后我们就可以先跑一遍多源最短路,同时记录一下离每个点最近的加油站。

将询问离然后求出加油站之间的最小生成树。加油站之间的边的边长也就是$dis[u]+dis[v]+w$,其中$w$是枚举的原图中每条边的边长。

一遍往里加边一遍搞询问就好了……

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<queue>
 5 #include<algorithm>
 6 #define N (200009)
 7 using namespace std;
 8 
 9 struct Node
10 {
11     int num,dis;
12     bool operator < (const Node &a) const {return dis>a.dis;}
13 };
14 struct Edge{int to,next,len;}edge[N<<1];
15 struct E{int x,y,l;}L[N<<1];
16 struct Que{int x,y,b,id;}Q[N];
17 int n,s,m,u,v,l,cnt,t;
18 int c[N],fa[N],dis[N],pre[N],vis[N],ans[N];
19 int head[N],num_edge;
20 priority_queue<Node>q;
21 bool cmp1(E a,E b) {return a.l<b.l;}
22 bool cmp2(Que a,Que b) {return a.b<b.b;}
23 
24 int Find(int x) {return x==fa[x]?x:fa[x]=Find(fa[x]);}
25 
26 void add(int u,int v,int l)
27 {
28     edge[++num_edge].to=v;
29     edge[num_edge].next=head[u];
30     edge[num_edge].len=l;
31     head[u]=num_edge;
32 }
33 
34 void Dijkstra()
35 {
36     memset(dis,0x7f,sizeof(dis));
37     for (int i=1; i<=s; ++i)
38     {
39         dis[c[i]]=0; pre[c[i]]=c[i];
40         q.push((Node){c[i],0});
41     }
42     while (!q.empty())
43     {
44         int x=q.top().num; q.pop();
45         if (vis[x]) continue;
46         vis[x]=true;
47         for (int i=head[x]; i; i=edge[i].next)
48             if (dis[x]+edge[i].len<dis[edge[i].to])
49             {
50                 dis[edge[i].to]=dis[x]+edge[i].len;
51                 pre[edge[i].to]=pre[x];
52                 q.push((Node){edge[i].to,dis[edge[i].to]});
53             }
54     }
55 }
56 
57 void Kruskal()
58 {
59     for (int i=1; i<=n; ++i)
60         for (int j=head[i]; j; j=edge[j].next)
61             if (pre[i]!=pre[edge[j].to])
62                 L[++cnt]=(E){pre[i],pre[edge[j].to],dis[i]+dis[edge[j].to]+edge[j].len};
63     sort(L+1,L+cnt+1,cmp1);
64     
65     int now=1;
66     for (int i=1; i<=n; ++i) fa[i]=i;
67     for (int i=1; i<=t; ++i)
68     {
69         while(now<=cnt && L[now].l<=Q[i].b)
70         {
71             int fx=Find(L[now].x),fy=Find(L[now].y);
72             if (fx!=fy) fa[fx]=fy; now++;
73         }
74         int fx=Find(Q[i].x),fy=Find(Q[i].y);
75         if (fx==fy) ans[Q[i].id]=1;
76         else ans[Q[i].id]=0;
77     }
78     for(int i=1;i<=t;i++)
79         if(ans[i]) puts("TAK");
80         else puts("NIE");
81 }
82 
83 int main()
84 {
85     scanf("%d%d%d",&n,&s,&m);
86     for (int i=1; i<=s; ++i)
87         scanf("%d",&c[i]);
88     for (int i=1; i<=m; ++i)
89     {
90         scanf("%d%d%d",&u,&v,&l);
91         add(u,v,l); add(v,u,l);
92     }
93     Dijkstra();
94     scanf("%d",&t);
95     for (int i=1; i<=t; ++i)
96         scanf("%d%d%d",&Q[i].x,&Q[i].y,&Q[i].b), Q[i].id=i;
97     sort(Q+1,Q+t+1,cmp2);
98     Kruskal();
99 }
posted @ 2018-12-18 19:07  Refun  阅读(331)  评论(0编辑  收藏  举报