题解 P5304 【[GXOI/GZOI2019]旅行者】
这里提供一种 分治/倍增 的解法。
首先我们可以想到一个
然后我们很容易搞出一个
我们考虑一下这个算法的瓶颈是什么?显然不是求最短路,而是枚举。
最后的答案肯定是点集中一个点
我们思考一下能否在枚举的时候每次“倍增(分治)”。
假设a数组为题目中给的点集,为了方便理解,我这里用线段树来解释,假设我们已经建好了a数组的线段树,第一次我们把a数组中,下标<=mid的都连到S,其余连到T:
跑一边
那么凡是
这样分治下去,只要
为什么这么做不会漏掉答案呢?
一句话:任意两个叶子在线段树上都有唯一的
实际上这颗线段树根本不用建出来(似乎建出来连边可以优化到
最优解是
Code:
#include<cstdio>
#include<cmath>
#include<queue>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
static char buf[100000],*pa,*pd;
#define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
inline int read(){
register int x(0),f(1);register char c(gc);
while(c>'9'||c<'0')f=c=='-'?-1:1,c=gc;
while(c>='0'&&c<='9')x=x*10+c-48,c=gc;
return f*x;
}
const int N=110000,M=510000;
struct edge{
int to,next;
ll w;
}e[M];
int head[150000],tot;
void add(int x,int y,ll z){
e[++tot].next=head[x];e[tot].to=y;head[x]=tot;e[tot].w=z;
}
int t,n,m,K,a[N];
const int S=0,T=N-1;
ll dis[N],ans;
#define re(a) memset((a),0,sizeof((a)))
void clear(){
re(a);re(head);tot=0;
}
struct node{
int u;
ll udis;
};
bool operator < (node x,node y){
return x.udis>y.udis;
}
priority_queue<node> q;
ll DJ(){
memset(dis,127,sizeof(dis));
dis[S]=0;
q.push((node){S,0});
register int i;
while(!q.empty()){
int u=q.top().u;ll udis=q.top().udis;q.pop();
if(udis!=dis[u])continue;
for(i=head[u];i;i=e[i].next)
if(dis[u]+e[i].w<dis[e[i].to]){
dis[e[i].to]=dis[u]+e[i].w;
q.push((node){e[i].to,dis[e[i].to]});
}
}
return dis[T];
}
int past[150000];
void work(){
register int i,j,l,r,mid;
ans=(ll)pow(2,63)-10;
memcpy(past,head,sizeof(past));
for(mid=1;mid<=K;mid<<=1){
for(l=0,r=mid<<1;l<=K;l+=r){
for(j=l+1;j<=l+mid;j++)if(j<=K)add(S,a[j],0);
for(j=l+mid+1;j<=l+r;j++)if(j<=K)add(a[j],T,0);
}
ans=min(ans,DJ());
tot-=K;
memcpy(head,past,sizeof(head));
for(l=0,r=mid<<1;l<=K;l+=r){
for(j=l+1;j<=l+mid;j++)if(j<=K)add(a[j],T,0);
for(j=l+mid+1;j<=l+r;j++)if(j<=K)add(S,a[j],0);
}
ans=min(ans,DJ());
tot-=K;
memcpy(head,past,sizeof(head));
}
cout<<ans<<'\n';
}
int main(){
t=read();
register int i,x,y,z;
while(t--){
n=read();m=read();K=read();
for(i=1;i<=m;i++){
x=read();y=read();z=read();add(x,y,z);
}
for(i=1;i<=K;i++){
a[i]=read();
}
work();
clear();
}
return 0;
}