题意:n个点,m条边(可能有重边),每条边对应权值w,初始有1价值,每次经过一条边乘上边权w,问从起点1到终点n,获得的最大价值。(模1e9+7)
题解:由于要取模,如果直接跑最长路可能导致有某一条路径可以直接到达终点,且这条路径所获得的价值比最长路获得的价值大。因此不能直接最长路。 想到要将乘积转换成加法,用边权和来表示这条边是否应该选中。那么如何转换呢?
数学上的log函数是个好东西啊。。。用log将边的乘积转换成边的和以后,就是一个朴素最长路算法(用spfa避免重边),如果边权和较大,那么这条边就要选中,那么再开一个数组ans记录log缩权前的边权的乘积,最后ans[n] 就是答案。
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#include<vector>
using namespace std;
#define inf 0x3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define mem(a,b) memset(a,b,sizeof(a));
#define lowbit(x) x&-x;
typedef long long ll;
const int mx = 1e6+5;
const int mod = 1000000007;
int n,m;
struct node{
int to,nxt;
double val;
ll V;
}e[mx*2];
int head[mx];
double dis[mx];
ll ans[mx];
int vis[mx];
int tot;
void addedge(int u,int v,double w,ll p){
e[tot].to = v;
e[tot].nxt = head[u];
e[tot].val = w;
e[tot].V = p;
head[u] = tot++;
}
void spfa(int u){
queue<int>q;
dis[1] = 0;
ans[1] = 1;
q.push(1);
vis[1] = 0;
while(!q.empty()){
int cur = q.front();
q.pop();
vis[cur] = 0;
for(int i = head[cur]; i != -1; i = e[i].nxt){
int v = e[i].to;
if(dis[cur] + e[i].val < dis[v]){
dis[v] = dis[cur] + e[i].val;
ans[v] = ans[cur] * e[i].V % mod;
if(!vis[v]){
vis[v] = 1;
q.push(v);
}
}
}
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
int u,v;
ll w;
tot = 0;
mem(head,-1);
mem(dis,inf);
mem(vis,0);
for(int i = 1; i <= m; i++){
scanf("%d%d%lld",&u,&v,&w);
addedge(u,v,-log(w),w);
}
spfa(1);
printf("%lld\n",ans[n]);
}
}