题目来源:
https://www.luogu.org/problemnew/show/P2384
题目描述:
题目背景
狗哥做烂了最短路,突然机智的考了Bosh一道,没想到把Bosh考住了...你能帮Bosh解决吗?
他会给你100000000000000000000000000000000000%10金币w
题目描述
给定n个点的带权有向图,求从1到n的路径中边权之积最小的简单路径。
输入输出格式
输入格式:
第一行读入两个整数n,m,表示共n个点m条边。 接下来m行,每行三个正整数x,y,z,表示点x到点y有一条边权为z的边。
输出格式:
输出仅包括一行,记为所求路径的边权之积,由于答案可能很大,因此狗哥仁慈地让你输出它模9987的余数即可。
废话当然是一个数了w
//谢fyszzhouzj指正w
对于20%的数据,n<=10。
对于100%的数据,n<=1000,m<=1000000。边权不超过10000。
输入输出样例
输入样例#1: 复制
3 3
1 2 3
2 3 3
1 3 10
输出样例#1: 复制
9
说明
好好看一看再写哟w
解题思路:
这题是最短路的变形,感觉不错,因为求的是积的最小值,不是和的最小值,关键还要取模,在松弛的时候取模显然会导致贪心失败,我们可以考虑log(n*m)=log(n)+log(m),我们将边权都取对数,然后跑一遍spfa并记录下路径,然后在通过路径进行计算结果取模。。。感觉思路不错。
代码:
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<stack>
#include<vector>
#include<cmath>
#include<cstdio>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e6+10;
const int mod=9987;
struct newt
{
int to,next;
double val;
}e[maxn];
int n,cnt,m,vis[1010],head[1010],tu[1010][1010],pre[1010];
double dis[1010];
void addedge(int u,int v,double w)
{
e[cnt].to=v;
e[cnt].val=w;
e[cnt].next=head[u];
head[u]=cnt++;
}
int main()
{
memset(head,-1,sizeof(head));
cnt=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n+1;i++)
dis[i]=1e9;
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
tu[a][b]=c;
double w= log (c);
addedge(a,b,w);
}
queue<int>q;
vis[1]=1;
q.push(1);
dis[1]=0;
while(!q.empty())
{
int now=q.front();
q.pop();
vis[now]=0;
for(int i=head[now];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(dis[v]>dis[now]+e[i].val)
{
dis[v]=dis[now]+e[i].val;
pre[v]=now;
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
ll ans=1;
int t=n;
while(t!=1)
{
ans=1ll*(ans%mod)*(tu[pre[t]][t]%mod);
while(ans>mod)ans-=mod;
t=pre[t];
}
printf("%lld\n",ans);
return 0;
}