深圳技术大学第六届程序设计竞赛 A题思路分享(带权限 Floyd,倍增)

https://cpc.csgrandeur.cn/csgoj/problemset/problem?pid=1511

题意概述

给定 \(n\) 个星系,每个星系 \(i\) 有互不相同的空间稳定度 \(A_i\)

任意两个星系 \(u, v\) 之间有一条航道,基础能量消耗为 \(W_{u,v}\)

一次「量子跃迁」定义为从某个起点 \(u\) 移动到终点 \(v\) (\(u \neq v\)),跃迁过程中可以经过若干中间星系 \(x\),对于路径上的任意中间星系 \(x\),必须满足 \(A_x < \min(A_u, A_v)\)

单次跃迁的能量消耗,等于经过边的 \(W\) 之和。

现有 \(Q\) 次询问,每次询问给出 \(S, T\),求在恰好 \(K\) 次跃迁的前提下从 \(S\) 到达 \(T\) 的最小总能量消耗。如果无法到达,输出 \(-1\)

\(3 \le N \le 150,1 \le K \le 10^9,1 \le Q \le 10^6\)

思路

先考虑建图。

记原图的距离矩阵为 \(W\),得到的图的矩阵为 \(adj\)。按 \(a_i\) 大小枚举中间点松弛,第 \(k\) 轮松弛之后,经过所有中间点 \(a_i\)\(\le a_{ord_k}\),因此可以对所有满足 \(\min(a_s,a_t) \gt a_{ord_k}\) 的点对更新 \(adj\)

建完图后,预处理倍增数组 \(dp\)\(dp[i][j][k]\) 表示从 \(i\) 出发跳 \(2^k\) 步到 \(j\) 的最小距离。然后枚举 \(K\) 的二进制有效位,此时必须跳跃 \(2^k\) 步,记答案数组为 \(res\),转移方程:

\[nres[i][j] = \min(nres[i][j],res[i][p]+dp[p][j][k]) \]

之后每个询问都可以 \(\mathcal{O}(1)\) 完成。

时间复杂度 \(\mathcal{O}(n^3 \log K)\)

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const ll INF = 9e18;

void solve(){
	int n,K;
	cin >> n >> K;

	vector<ll> a(n+1);
	for (int i=1;i<=n;i++){
		cin >> a[i];
	}

	vector<vector<ll>> W(n+1,vector<ll>(n+1));	
	for (int i=1;i<=n;i++){
		for (int j=1;j<=n;j++){
			cin >> W[i][j];

			if (W[i][j]==-1){
				W[i][j] = INF;
			}
		}
	}

	vector<int> ord(n+1);
	iota(ord.begin(),ord.end(),0);
	sort(ord.begin()+1,ord.end(),[&](int i,int j){
		return a[i]<a[j];
	});

	auto adj = W;

	for (int tt=1;tt<=n;tt++){
		for (int i=1;i<=n;i++){
			if (W[i][ord[tt]]==INF) continue;

			for (int j=1;j<=n;j++){
				if (W[ord[tt]][j]==INF) continue;

				W[i][j] = min(W[i][j],W[i][ord[tt]]+W[ord[tt]][j]);

				if (a[ord[tt]]<min(a[i],a[j])){
					adj[i][j] = min(adj[i][j],W[i][j]);
				}									
			}
		}
	}

	vector<vector<vector<ll>>> dp(n+1,vector<vector<ll>>(n+1,vector<ll>(32,INF)));
	for (int i=1;i<=n;i++){
		for (int j=1;j<=n;j++){
			if (i==j) continue;
			
			dp[i][j][0] = min(dp[i][j][0],adj[i][j]);	
		}
	}

	for (int k=1;k<32;k++){
		for (int p=1;p<=n;p++){
			for (int i=1;i<=n;i++){
				if (dp[i][p][k-1]==INF) continue;

				for (int j=1;j<=n;j++){
					if (dp[p][j][k-1]==INF) continue;

					dp[i][j][k] = min(dp[i][j][k],dp[i][p][k-1]+dp[p][j][k-1]);
				}
			}
		}
	}

	vector<vector<ll>> res(n+1,vector<ll>(n+1,INF));

	for (int i=1;i<=n;i++){
		res[i][i] = 0;
	}

	for (int k=31;k>=0;k--){
		if (K>>k&1){
			vector<vector<ll>> nres(n+1,vector<ll>(n+1,INF));

			for (int p=1;p<=n;p++){
				for (int i=1;i<=n;i++){
					if (res[i][p]==INF) continue;

					for (int j=1;j<=n;j++){
						if (dp[p][j][k]==INF) continue;

						nres[i][j] = min(nres[i][j],res[i][p]+dp[p][j][k]);
					}
				}
			}

			res = nres;
		}
	}

	int q;
	cin >> q;

	while (q--){
		int s,t;
		cin >> s >> t;

		if (res[s][t]==INF){
			cout << -1 << '\n';
		}
		else{
			cout << res[s][t] << '\n';
		}
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t = 1;
	// cin >> t;
	while (t--) solve();

	return 0;
}
posted @ 2026-05-12 12:54  kzssCCC  阅读(24)  评论(0)    收藏  举报