题解 P9370 [APIO2023] 赛博乐园 / cyberland
Solution
观察到 \(K\le 30\) 占了 \(97\) 分,盲猜后面的是诈骗。
由于“将已经累积的权值减半”这种操作比较抽象,考虑将其转化为比较好做的操作。反向建图,从终点倒着走回起点,发现减半操作其实对应着将后面的边权值减半,结合 \(K\le 30\) 很容易想到分层图,第 \(k\) 层的边权自然是 \(w\times2^{-k}\),同时这样建图后还可以看出当 \(k\) 过大时 \(w\times2^{-k}\) 已经可以忽略不计了,实际上当 \(K\) 取到 \(70\) 左右时就已经有 \(\sum w_{\max}\times2^{-k}\le 10^{-6}\) 了,故 \(K\) 可以与 \(70\) 取 \(\min\)。
然后考虑归零操作,注意到原图上任意一个可归零的点都可当作起点,则在反图上这些点都可当作终点。
额外要注意的是,由于不允许离开终点,还要特判掉从家不经过终点无法走到零点的情况,这个是可以预处理的。
那么就做完了,提一些小优化:
- 可以不用直接把 \(mK\) 大小的图建出来,在跑 Dij 的时候再把边权推出来。
- 由于我们找的是单个源点到一个集合的任意一点的最短路,由 Dij 的贪心特性,到达第一个集合内的点即可退出。
时间复杂度 \(O(mK\log nK)\),正常写法就拿下最优解。
Code
#include<bits/stdc++.h>
using namespace std;
#define f(x,y)(n*(x)+(y))
const int N=1e5+10;
const long double inf=2e18,eps=1e-7;
struct node{
int v;
long double w;
friend bool operator<(node x,node y){
return x.w>y.w;
}
};
struct edge{
int v;
long double w;
int nxt;
}e[N<<1];
long double dis[N*72];
long double t[72];
bool vis[N];
int tot,head[N];
void add(int u,int v,long double w){
e[++tot]={v,w,head[u]};
head[u]=tot;
}
void bfs(int s,int h){
queue<int>q1;
q1.push(s);
vis[s]=1;
while(!q1.empty()){
int x=q1.front();
q1.pop();
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].v;
if(!vis[v]&&v!=h){
vis[v]=1;
q1.push(v);
}
}
}
}
void dij(int s,int n,int k,vector<int>&arr){
priority_queue<node>q2;
for(int i=0;i<=f(k,n);i++){
dis[i]=inf;
}
q2.push({s,0});
dis[s]=0;
while(!q2.empty()){
node tmp=q2.top();
q2.pop();
int x=tmp.v;
long double val=tmp.w;
if(val>dis[x]){
continue;
}
if(x%n==0||(!arr[x%n]&&vis[x%n])){
return;
}
for(int i=head[x%n];i;i=e[i].nxt){
int v=f(x/n,e[i].v);
long double w=e[i].w/t[x/n];
if(e[i].v==s){
continue;
}
if(dis[v]>dis[x]+w+eps){
dis[v]=dis[x]+w;
q2.push({v,dis[v]});
}
if(arr[v%n]==2&&x/n<k){
v=f(x/n+1,e[i].v);
if(dis[v]>dis[x]+w+eps){
dis[v]=dis[x]+w;
q2.push({v,dis[v]});
}
}
}
}
}
double solve(int n,int m,int k,int h,vector<int>u,vector<int>v,vector<int>w,vector<int>arr){
k=min(k,70);
t[0]=1;
for(int i=1;i<=k;i++){
t[i]=t[i-1]*2.0;
}
for(int i=0;i<n;i++){
head[i]=0;
vis[i]=0;
}
for(int i=0;i<m;i++){
add(u[i],v[i],w[i]);
add(v[i],u[i],w[i]);
}
bfs(0,h);
dij(h,n,k,arr);
long double ans=inf;
for(int i=0;i<n;i++){
if((i&&arr[i])||!vis[i]){
continue;
}
for(int j=0;j<=k;j++){
ans=min(ans,dis[f(j,i)]);
}
}
if(ans>1e18){
ans=-1;
}
return ans;
}

浙公网安备 33010602011771号