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