版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/81812152
【题目】
原题地址
题目大意:一个有向图,走每条边有个代价,且花费的时间
有个概率
,从1到
,若到达时时间超过
,则需要额外
的花费,问期望最小花费。
【题目分析】
myy论文题。
【解题思路】
来膜拜一发myy论文题。
首先考虑暴力dp,
表示到达
点,花费时间
期望的最小代价。那么对于
出发的每条边
,设
为走这条边概率,那么我们可以得到:
用一个柿子 表示 时刻从 出发到达 点的最小期望费用,那么上面的柿子就可以写成:
由于一个点不可能回到祖先,所以这幅图是一个DAG,我们可以直接dp,状态数是 ,转移 ,果断跪掉。
当然如果我们将 数组翻转,实际上可以得到一个卷积的形式,可以用 优化。
这样当然也过不了。因为右边的柿子和时间有关,我们可以考虑对时间分治。
这里给出一个结论:
即计算卷积的时候,可以将贡献拆成几个部分来计算。
实际上推到这里我就已经晕了- -,不过还是要坚持(看别人blog)。
分治过程比较奇怪,是先递归右边的。
1.对于当前的分治层[
,我们先递归处理右边
,得到右边所有的
,然后用FFT求值去贡献到
。
2.继续递归处理左边
。
3.当到达递归最底层即
时,我们用
去求出所有的
,因为此时的转移来源
都计算完毕。
由于对于
的情况,已经超时所以我们不用管了,那么
的数组实际上只用开
就行了。
这个下标好奇怪啊- -
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef double db;
const db pi=acos(-1);
const int INF=1e9;
const int N=2e5+10,M=110,K=4e4+10;
int n,m,L,S,T,X,rev[N];
db p[M][N],d[M][M],f[M][K],g[M][K];
int read()
{
int ret=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
while(isdigit(c)) {ret=ret*10+(c^48);c=getchar();}
return f?ret:-ret;
}
struct Tway{int u,v,w;}e[N];
struct cd
{
db r,i;
cd(){}
cd(db rr,db ii){r=rr;i=ii;}
cd operator + (const cd&A)const {return cd(r+A.r,i+A.i);}
cd operator - (const cd&A)const {return cd(r-A.r,i-A.i);}
cd operator * (const cd&A)const {return cd(r*A.r-i*A.i,r*A.i+i*A.r);}
cd operator / (const db&A)const {return cd(r/A,i/A);}
}a[N],b[N];
void fft(cd *a,int n,int f)
{
for(int i=0;i<n;++i) if(i>rev[i]) swap(a[i],a[rev[i]]);
for(int i=1;i<n;i<<=1)
{
cd wn(cos(pi/i),f*sin(pi/i));
for(int j=0;j<n;j+=(i<<1))
{
cd w(1,0);
for(int k=0;k<i;++k,w=w*wn)
{
cd x=a[j+k],y=w*a[i+j+k];
a[j+k]=x+y;a[i+j+k]=x-y;
}
}
}
if(f==-1) for(int i=0;i<n;++i) a[i]=a[i]/n;
}
void init(int l,int r)
{
int mid=(l+r)>>1;
for(int j=1;j<=m;++j)
{
for(L=0,S=1;S<=(r<<1)-l-mid-1;S<<=1,++L);
for(int i=0;i<S;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
for(int i=0;i<S;++i) a[i]=b[i]=cd(0,0);
for(int i=mid+1;i<=r;++i) a[i-mid-1].r+=f[e[j].v][i];
for(int i=1;i<=r-l;++i) b[r-l-i].r+=p[j][i];
fft(a,S,1);fft(b,S,1);
for(int i=0;i<S;++i) a[i]=a[i]*b[i];
fft(a,S,-1);
for(int i=l;i<=mid;++i) g[j][i]+=a[i-mid-1+r-l].r;
}
}
void solve(int l,int r)
{
if(l==r)
{
for(int i=1;i<=m;++i) f[e[i].u][l]=min(f[e[i].u][l],g[i][l]+e[i].w);
return;
}
int mid=(l+r)>>1;
solve(mid+1,r);init(l,r);solve(l,mid);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("CF553E.in","r",stdin);
freopen("CF553E.out","w",stdout);
#endif
n=read();m=read();T=read();X=read();
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(i^j) d[i][j]=INF;
for(int i=1;i<=m;++i)
{
e[i].u=read();e[i].v=read();e[i].w=read();
d[e[i].u][e[i].v]=min(d[e[i].u][e[i].v],1.0*e[i].w);
for(int j=1;j<=T;++j) p[i][j]=(db)read()/1e5;
}
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
for(int i=0;i<=T<<1;++i) f[n][i]=i<=T?0:X;
for(int i=1;i<n;++i)
for(int j=0;j<=T<<1;++j)
f[i][j]=j<=T?INF:d[i][n]+X;
init(1,T<<1);solve(0,T);
printf("%.10lf\n",f[1][0]);
return 0;
}
【总结】
myy!myy!