题目大意
一个图n个点,有若干条边,每条边都有成功通过的概率和花费,每条边只能通过一次,即使通过失败,也算通过一次。
其中第
还有m条边从
求从点1到点
0≤p≤1.0,1≤n,m≤105,1≤wi≤100,1≤ci≤100
题目分析
我们将所有边一起讨论,概率为p,花费为
考虑处理当前点x到
假设它连出的门为s1...sk,设一个1至
Ex=min{csa1+psa1Esa1+(1−psa1)min{csa2+psa2Esa2+(1−psa2)min{...}}}
这个怎么求最小啊?一脸懵逼。
可以发现两个相邻位置交换顺序并不会影响前面和后面的排列的答案,所以我们只用考虑相邻两个位置谁在前面。
x在
A(cx+pxEx+(1−px)(cy+pyEy+(1−py)B))<A(cy+pyEy(1−py)(cx+pxEx+(1−px)B))
令Fx=cx+pxEx化简可得
Fxpx<Fypy
然后就可以排序解决了。
时间复杂度O(n+mlog2m)。
代码实现
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=100005;
const int M=100005;
const int E=N+M;
struct W
{
double F,P;
}ways[E];
bool operator<(W a,W b)
{
return a.F/a.P<b.F/b.P;
}
int cnt;
int tov[E],next[E];
double p[E],e[N];
bool vis[N];
int last[N];
int w[E];
int n,m,tot;
void insert(int x,int y,double pb,int ws)
{
tov[++tot]=y;
p[tot]=pb;
w[tot]=ws;
next[tot]=last[x];
last[x]=tot;
}
void dfs(int x)
{
if (vis[x])
return;
vis[x]=true;
int i=last[x],y;
while (i)
{
y=tov[i];
dfs(y);
ways[++cnt].P=p[i];
ways[cnt].F=(double)w[i]+p[i]*e[y];
i=next[i];
}
i=last[x];
cnt=0;
while (i)
{
y=tov[i];
ways[++cnt].P=p[i];
ways[cnt].F=(double)w[i]+p[i]*e[y];
i=next[i];
}
sort(ways+1,ways+1+cnt);
double Pn=1.0;
for (int i=1;i<=cnt;i++)
{
e[x]=e[x]+Pn*ways[i].F;
Pn=Pn*(1-ways[i].P);
}
}
int main()
{
freopen("portal.in","r",stdin);
freopen("portal.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1,wr;i<n;i++)
{
scanf("%d",&wr);
insert(i,i+1,1.0,wr);
}
for (int i=1;i<=m;i++)
{
int x,y,wr;
double pr;
scanf("%d%d%lf%d",&x,&y,&pr,&wr);
insert(x,y,pr,wr);
}
vis[n]=true;
e[n]=0;
dfs(1);
printf("%.2lf\n",e[1]);
fclose(stdin);
fclose(stdout);
return 0;
}