题解:P9370 [APIO2023] 赛博乐园 / cyberland
题目传送门
题目大意
有 \(N\) 个国家,\(M\)条双向道路,每一条道路的花费为 \(c_i\),有些国家有超能力,用 \(arr_i\) 来表示第 \(i\) 个国家的超能力编号,若 \(arr_i=0\),表示第 \(i\) 个国家可以把之前的所有花费清零,若 \(arr_i=1\) 表示这个国家没有超能力,若 \(arr_i=2\) 表示这个国家可以把当前花费除以 \(2\),但是这个能力最多用 \(k\) 次。国家可以重复经过,每次经过都可以选择是否使用超能力,至多用一次。求从 \(0\) 号点到 \(H\) 的最小花费,误差 \(<10^{-6}\)。
思路
容易发现当 \(k>70\) 时,误差已经小于 \(10^{-6}\)。所以我们想到用分层图跑 dijkstra, 以 \(0\) 号点和所有的\(arr_i=0\) 的点为起点,对于 \(arr_i=2\) 的国家记得 \(dis \div 2\),其他国家正常跑最短路。优先队列排序时以层数为第一关键字,花费为第二关键字。
思路比较清晰,但是代码有很多细节。注意是多测,要及时清空,特别是链式前向星,一定要把 \(head\) 数组归零。还有要先跑一遍 dfs 判断连通性。不建议使用 memset 清空数组,很慢,建议直接用 for。vector 存图也有点慢,链式前向星会快一点。
代码
#include<bits/stdc++.h>
#include "cyberland.h"
#define INF 1e18
using namespace std;
const int N=101010;
int vis[70*N],head[70*N],tot,n,h,k;
double dis[70*N];
int id(int x,int y){return x+y*n;}
struct tt{
int to,w,next,div;
}e[200*N];
struct node{
int id;
double val;
bool operator < (const node &a)const{
if(id/n!=a.id/n)return id/n>a.id/n;
return val>a.val;
}
};
void add(int u,int v,int w,int div){e[++tot]=(tt){v,w,head[u],div},head[u]=tot;}
priority_queue<node>q;
void dfs(int u){
vis[u]=1;
if(u==h)return;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(vis[v])continue;
dfs(v);
}
}
void init(){
for(int i=0;i<=n*(k+1)+10;i++)vis[i]=0,head[i]=0,dis[i]=INF;
tot=0;
}
double solve(int N,int M,int K,int H,std::vector<int>x,std::vector<int>y,std::vector<int>c,std::vector<int>arr){
K=min(K,68);
n=N,h=H,k=K;
init();
for(int i=0;i<M;i++){
add(x[i],y[i],c[i],1);
add(y[i],x[i],c[i],1);
}
dfs(0);
if(!vis[H])return -1;
for(int i=0;i<=(n+1)*k+1;i++)head[i]=0;
tot=0;
for(int i=0;i<M;i++){
for(int j=0;j<=K;j++){
if(x[i]!=H)add(id(x[i],j),id(y[i],j),c[i],1);
if(y[i]!=H)add(id(y[i],j),id(x[i],j),c[i],1);
if(j!=K&&x[i]!=H&&arr[y[i]]==2)add(id(x[i],j),id(y[i],j+1),c[i],2);
if(j!=K&&y[i]!=H&&arr[x[i]]==2)add(id(y[i],j),id(x[i],j+1),c[i],2);
}
}
for(int i=0;i<N;i++)if(!arr[i]&&vis[i])q.push((node){i,0}),dis[i]=0;
for(int i=0;i<=(N+1)*K+10;i++)vis[i]=0;
q.push((node){0,0});
dis[0]=0;
while(!q.empty()){
node now=q.top();
int u=now.id;
q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
int w=e[i].w;
int d=e[i].div;
if((dis[u]+w)/d<dis[v]){
dis[v]=(dis[u]+w)/d;
if(!vis[v])q.push((node){v,dis[v]});
}
}
}
double ans=INF;
for(int i=0;i<=K;i++)ans=min(ans,dis[id(H,i)]);
return ans;
}

浙公网安备 33010602011771号