1. 程式人生 > 實用技巧 >【題解】 P2384 最短路(邊權之積最短路)

【題解】 P2384 最短路(邊權之積最短路)

T1 P2384 最短路(邊權之積最短路)

題目背景

狗哥做爛了最短路,突然機智的考了 Bosh 一道,沒想到把 Bosh 考住了...你能幫 Bosh 解決嗎?

題目描述

給定 nn 個點的帶權有向圖,求從 11 到 nn 的路徑中邊權之積最小的簡單路徑。

輸入格式

第一行讀入兩個整數 nn,mm,表示共 nn 個點 mm 條邊。 接下來 mm 行,每行三個正整數 xx,yy,zz,表示點 xx到點 yy 有一條邊權為z的邊。

輸出格式

輸出僅包括一行,記為所求路徑的邊權之積,由於答案可能很大,因此狗哥仁慈地讓你輸出它模 99879987 的餘數即可。

廢話當然是一個數了w

//謝fyszzhouzj指正w

輸入輸出樣例

輸入 #1

3 3
1 2 3 
2 3 3 
1 3 10

輸出 #1

9

說明/提示

對於 20%20% 的資料,n\leq 10n≤10。

對於 100%100% 的資料,n\leq 10^3n≤103,m\leq 10^6m≤106。邊權不超過 10^4104。

發現乘法和加法一樣是可以用迪傑斯特拉的貪心來實現乘法最短路的。

但是這樣乘起來會爆ll,無法儲存,所以對乘法的結果進行hash,dis儲存邊權乘積取log後的結果。

不帶負環的最短路一定是簡單路徑,即每個節點最多隻經過一遍。在鬆弛操作的時候,pre[終點v] = pre[起點u]表示v是由u過來的,preed[v] = 邊的編號e,從而儲存下來邊權。最後我們遍歷最短路,求出答案即可。

#include<cmath>
#include<queue>
#include<cstdio>
#include<string>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 0x3fffffff
#define mod 9987
using namespace std;
const int maxn = 1010;
const int maxm = 2000010; 
struct Edge{
	int to, nxt, k;
}ed[maxm];
int first[maxn], en, n, m, k, u, v;
void add(int u, int v, int k){
	ed[++en].to = v;
	ed[en].nxt = first[u];
	ed[en].k = k;
	first[u] = en;
}
int dis[maxn], vst[maxn];
priority_queue<pair<int, int> > q;

int pre[maxn], preed[maxn]; 
void dijkstra(int s){
	memset(dis, 0x3f, sizeof dis);
	memset(vst, 0, sizeof(vst));
	dis[s] = 0;
//	vst[s] = 1;
	q.push({0, s});
	while(q.size()){
		int u = q.top().second;
		q.pop();
		if(vst[u])	continue;
		vst[u] = 1;
		for(int e = first[u]; e; e = ed[e].nxt){
			int v = ed[e].to, k = ed[e].k;
			if(dis[v] > dis[u] + log(k)){
				dis[v] = dis[u] + log(k);
				preed[v] = e;
				pre[v] = u;
				if(!vst[v]){
//					vst[v] = 1;
					q.push({-dis[v], v} );
				}
			}
		} 
	}
}
ll ans = 1;
int main(){
	cin >> n >> m;
	for(int i = 1; i <= m; i++){
		scanf("%d%d%d", &u, &v, &k);
		add(u, v, k);
	} 
	dijkstra(1);
//	for(int i = 1; i <= n; i++)	cout << dis[i] << " ";
//	cout << endl; 
	int pos = n;
	while(pos > 1){
//		cout << ed[preed[pos]].to << endl;
		ans *= ed[preed[pos]].k;
		ans %= mod;
		pos = pre[pos];
	}
	cout << ans << endl;
	return 0;
}