[GXOI/GZOI2019] 旅行者
题目传送门
题解
Solution \(1\)
考虑有 \(s\) 个源点与 \(t\) 个汇点,求这些源点与汇点两两之间的最小最短路距离的做法。显然可以建一个超级源点和超级汇点。
现在问题转化成如何把这些点分为若干组,使任意两个点都被分为不同的组一次。
考虑二进制分组,具体地,枚举每个二进制位,当前位为 \(0\) 的分为一组,为 \(1\) 分为另外一组。因为两个不相同的数一定有一位二进制位不相同,所以满足条件。
时间复杂度 \(O(m \times \log^2 n)\)。
void dij(){
for(int i=0;i<=n+1;i++) dis[i]=INT_MAX,vis[i]=false;
dis[0]=0,q.push({0,0});
while(!q.empty()){
pr x=q.top();q.pop();
if(vis[x.second]) continue;
vis[x.second]=true;
for(pr i:g[x.second]){
int dao=i.first,val=i.second;
if(dis[dao]>x.first+val){
dis[dao]=x.first+val;
q.push({dis[dao],dao});
}
}
}
}
void solve(int ce){
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
g[u].push_back({v,w});
}
for(int i=1;i<=k;i++) Q[i]=read();
int ans=INT_MAX;
for(int i=0;i<=15;i++){
int x=1<<i;
for(int j=1;j<=k;j++){
if(Q[j]&x) g[0].push_back({Q[j],0});
else g[Q[j]].push_back({n+1,0});
}
dij();ans=min(ans,dis[n+1]);
g[0].clear();
for(int j=1;j<=k;j++)
if(!(Q[j]&x)) g[Q[j]].pop_back();
for(int j=1;j<=k;j++){
if(Q[j]&x) g[Q[j]].push_back({n+1,0});
else g[0].push_back({Q[j],0});
}
dij();ans=min(ans,dis[n+1]);
g[0].clear();
for(int j=1;j<=k;j++)
if(Q[j]&x) g[Q[j]].pop_back();
}
cout<<ans<<"\n";
for(int i=1;i<=n;i++) g[i].clear();
}
Solution \(2\)
考虑最短路的产生过程,显然我们可以枚举每个边,找到所有关键点到 \(u\) 的最短路加上 \(v\) 到所有关键点的最短路再加上边权,用这个来更新答案。只需要在正图上跑一遍 dij,在反图上也跑一遍即可。
由于不能从自己到自己,所以记录当前点的最短路是从哪个关键点来的,如果 \(from_u \ne from_v\),那么可以更新答案。
证明正确性:考虑能成为答案的最短路路径,设两端分别为 \(U\) 和 \(V\)。因为是正权图,所以其路径中一定没有别的关键点,所以一定有一个临界边,即 \(dis_{U,u}<dis_{u,V}\) 且 \(dis_{U,v} > dis_{v,V}\),即 \(from_u=U\) 且 \(from_v=V\)。所以一定能取到答案。
时间复杂度:\(O(m \log n)\)。
struct node{
vector<pr > g[N];
int dis[N],f[N];
bool vis[N];
priority_queue<pr,vector<pr >,greater<pr > > q;
void dij(){
for(int i=1;i<=n;i++) dis[i]=INT_MAX,vis[i]=false;
vis[0]=false,dis[0]=0,q.push({dis[0],0});
while(!q.empty()){
pr x=q.top();q.pop();
if(vis[x.second]) continue;
vis[x.second]=false;
for(pr i:g[x.second]){
int dao=i.first,val=i.second;
if(dis[dao]>x.first+val){
dis[dao]=x.first+val;
f[dao]=mk[dao]?dao:f[x.second];
q.push({dis[dao],dao});
}
}
}
}
}tu[2];
void solve1(int ce){
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
tu[0].g[u].push_back({v,w});
tu[1].g[v].push_back({u,w});
}
for(int i=1;i<=k;i++) Q[i]=read();
for(int i=1;i<=k;i++)
tu[0].g[0].push_back({Q[i],0}),
tu[1].g[0].push_back({Q[i],0}),
mk[Q[i]]=true;
tu[0].dij();tu[1].dij();
int ans=INT_MAX;
for(int i=1;i<=n;i++){
for(pr j:tu[0].g[i]){
if(tu[0].f[i]==tu[1].f[j.first]) continue;
ans=min(ans,tu[0].dis[i]+j.second+tu[1].dis[j.first]);
}
}
cout<<ans<<"\n";
for(int j=0;j<=1;j++)
for(int i=0;i<=n;i++)
tu[j].g[i].clear(),mk[i]=false,tu[j].f[i]=0;
}

浙公网安备 33010602011771号