【题解】 P2384 最短路(边权之积最短路)
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,从而存储下来边权。最后我们遍历最短路,求出答案即可。
由于是存对数,所以dis要double。
在取余的过程中,ans可能模9987后等于0,此时我们要让ans=1,然后接着乘。
真正ans=0的是n=1只有1个点的,此时ans=0。
#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;
}
bool vst[maxn];
double dis[maxn];
priority_queue<pair<int, int> > q;
int pre[maxn], preed[maxn];
void dijkstra(int s){
// memset(dis, 0x3f, sizeof dis);
for(int i = 1; i <= n; i++) dis[i] = 1061169567;
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);
}
if(n == 1){
cout << 0 << endl;
return 0;
}
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;
if(ans == 0) ans = 1;
}
cout << ans << endl;
return 0;
}