题解 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\)。

然后考虑归零操作,注意到原图上任意一个可归零的点都可当作起点,则在反图上这些点都可当作终点。

额外要注意的是,由于不允许离开终点,还要特判掉从家不经过终点无法走到零点的情况,这个是可以预处理的。

那么就做完了,提一些小优化:

  1. 可以不用直接把 \(mK\) 大小的图建出来,在跑 Dij 的时候再把边权推出来。
  2. 由于我们找的是单个源点到一个集合的任意一点的最短路,由 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;
}
posted @ 2025-06-07 21:43  Z3k7223  阅读(40)  评论(0)    收藏  举报