CF-gym-103055-Shortest Path Query-最短路+位运算+树

There is an undirected graph with n vertices and m edges. The vertices are labelled by 1,2,…,n. The i-th edge connects the ui-th vertex and the vi-th vertex, the length of which is wi. Here, ui’s binary representation is always a prefix of vi’s binary representation. Both binary representations are considered without leading zeros. For example, ui=210=102, vi=510=1012.

You will be given q queries. In the i-th query, you will be given two integers si and ti. Please write a program to figure out the length of the shortest path from the si-th vertex to the ti-th vertex, or determine there is no path between them.

Input
The input contains only a single case.

The first line of the input contains two integers n and m (1≤n≤100000, 1≤m≤200000), denoting the number of vertices and the number of edges.

In the next m lines, the i-th line (1≤i≤m) contains three integers ui,vi and wi (1≤ui<vi≤n, 1≤wi≤109), describing the i-th edge. It is guaranteed that ui’s binary representation is a prefix of vi’s binary representation.

In the next line, there contains a single integer q (1≤q≤200000), denoting the number of queries.

In the next q lines, the i-th line (1≤i≤q) contains two integers si and ti (1≤si,ti≤n, si≠ti), describing the i-th query.

Output
For each query, print a single line containing an integer, denoting the length of the shortest path. If there is no path, print “-1” instead.

cf题目

题意:
现有n个结点,若干条无向边,
两个结点之间直接连有边的必要条件是:
节点序号小的是节点序号大的结点的二进制前缀
(如5:101和 21:10101)
现在有q个询问,每次询问输入两个结点f,t
对于每次询问输出f,t之间最短路(不存在则输出-1)

题解:
因为数据量级的限制,不能用floyd或者floyd+dp计算,
但是仍然可以用floyd和dp的思想来计算每个询问的结果

先用任意优化最短路算法计算出以某个点为根节点,其所有可能的后缀结点到根此节点的最短路,用dp[i][j]代表结点i到结点i>>j的最短距离,每次跑dfs搜索生成树并更新最短路径,因为跑子树时将无向边看作有向边,所以更新最短路复杂度大大降低,
预处理每个结点为源,最终复杂度为o(nlogm)

每次询问输入x和y,当x,y在同一颗已有树上时(即x是y前缀或y是x前缀),可直接得出最短路,若xy不在同一棵前缀树上,则寻找k=lca(x,y),(即x和y的最大公共前缀)更新dis[x][y]=min(dp[x][k1]+dp[y][k2]),每次使k>>=1遍历x和y的所有前缀,更新完毕,询问复杂度为o(qlogn)。


#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
const ll N = 1e5 + 17, inf = 1e12;
vector<P>e[N];
ll n, m, u, v, w, q;
ll dp[N][30];
ll vis1[N], vis2[N], dis[N];
inline ll bit(ll u, ll v) {
	ll i = 1;
	for (; i < 30&&(v>>i); i++) 
		if (u == (v>>i))return i;
	return 0;
}
P lca(ll u, ll v) {
	ll i = 0, j = 0;
	ll flag = 0;
	for (i = 0; i < 30 && (u >> i); i++) {
		for (; j < 30 && (v >> j) >= (u >> i); j++)
			if ((u >> i) == (v >> j)) {
				flag++; break;
			}
		if (flag)break;
	}
	return P{ i,j };
}
void dijkstra(ll root) {
	priority_queue<P, vector<P>, greater<P> >que;
	que.push(P{ 0,root });
	dp[root][0] = 0;
	while (!que.empty()) {
		ll u = que.top().second, d = que.top().first;
		que.pop();
		if (vis1[u] == root)continue;
		vis1[u] = root;
		dis[u] = d; ll b = bit(root, u);
		dp[u][b] = min(dp[u][b], dis[u]);
		for (auto ed : e[u]) {
			ll t = ed.first;
			if (t <= root)continue;
			if (vis2[t] != root) {
				dis[t] = inf;
				vis2[t] = root;
			}
			if (dis[t] > dis[u] + ed.second) {
				dis[t] = dis[u] + ed.second;
				que.push(P{ dis[t],t });
			}
		}
	}
}
int main() {
	cin >> n >> m;
	for (ll i = 1; i <= n; i++)
		for (ll j = 0; j < 30; j++)dp[i][j] = inf;
	for (ll i = 1; i <= m; i++) {
		scanf("%lld %lld %lld", &u, &v, &w);
		//cin >> u >> v >> w;
		ll b = bit(u, v);
		e[v].push_back(P{ u,w });
		e[u].push_back(P{ v,w });
		dp[v][b] = w;
	}
	cin >> q;
	for (ll i = 1; i <= n; i++)dijkstra(i);
	for (ll i = 1; i <= q; i++) {
		scanf("%lld %lld", &u, &v);
		//cin >> u >> v;
		ll len = inf;
		if (u > v)swap(u, v);
		P b = lca(u, v);
		ll b1 = b.first, b2 = b.second;
		while (u >> b1) {
			len = min(len, dp[u][b1] + dp[v][b2]);
			b1++, b2++;
		}
		if (len >= inf)printf("-1\n");
		else printf("%lld\n", len);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值