正题
题目链接:https://www.luogu.com.cn/problem/P5304
题目大意
nnn个点mmm条边的一张有向图,有kkk个关键点,求距离最短的一对关键点。
解题思路
我们能够O(nlogn)O(n\log n)O(nlogn)的进行一个起点或多个起点同时的最短路,但是这题显然不能枚举其中的一对点。
可以考虑多个起点的最短路做法,我们可以做到每次计算一组点到另一组点之间的最短路,考虑如何分组能够让每一队都分到过不同的组。
我们可以枚举一个位数iii,对于第xxx个关键点,如果xxx的第iii位为111那么久分入第一个点组,否则分入第二个点组,那么这样分组就可以做到每个点对都分到过不同的组。
时间复杂度O(nlog2n)O(n\log^2 n)O(nlog2n)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const ll N=1e5+10;
struct edge_node{ll x,y,w;
}e[N*5];
struct node{ll to,next,w;
}a[N*7];
struct point{ll pos,dis;bool operator<(const point &x)const{return x.dis<dis;}
};
priority_queue<point> q;
ll T,n,m,k,tot,ans,s,t;
ll z[N],ls[N],f[N];
bool v[N];
void addl(ll x,ll y,ll w){a[++tot].to=y;a[tot].next=ls[x];a[tot].w=w;ls[x]=tot;
}
void Dij(){memset(f,0x3f,sizeof(f));memset(v,0,sizeof(v));q.push((point){s,0});f[s]=0;while(!q.empty()){ll x=q.top().pos,w=q.top().dis;q.pop();if(v[x])continue;v[x]=1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(f[x]+a[i].w<f[y]){f[y]=f[x]+a[i].w;if(!v[y])q.push((point){y,f[y]});}}}return;
}
int main()
{scanf("%lld",&T);while(T--){scanf("%lld%lld%lld",&n,&m,&k);s=0;t=n+1;ans=1e18;for(ll i=1;i<=m;i++)scanf("%lld%lld%lld",&e[i].x,&e[i].y,&e[i].w);for(ll i=1;i<=k;i++)scanf("%lld",&z[i]);for(ll p=0;(1<<p)<=k;p++){tot=0;memset(ls,0,sizeof(ls));for(ll i=1;i<=m;i++)addl(e[i].x,e[i].y,e[i].w);for(ll i=1;i<=k;i++)if((i>>p)&1)addl(s,z[i],0);else addl(z[i],t,0);Dij();ans=min(ans,f[t]);tot=0;memset(ls,0,sizeof(ls));for(ll i=1;i<=m;i++)addl(e[i].x,e[i].y,e[i].w);for(ll i=1;i<=k;i++)if((i>>p)&1)addl(z[i],t,0);else addl(s,z[i],0);Dij();ans=min(ans,f[t]);}printf("%lld\n",ans);}
}