FJNUOJ A.the greed of Yehan 最长路+log缩权

题意: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]);
	}
}

猜你喜欢

转载自blog.csdn.net/pall_scall/article/details/82357034