Link
设\(f_{u,i}\)表示\(i\)时刻到\(u\)的最小答案,那么我们有:\(f_{u,i}=\min\limits_{(u,v,w,id)\in E}(w+\sum\limits_{j=0}^tf_{v,,i+j}p_{id,j})\)。
令\(g_{e,i}\)表示\(i\)时刻走上\(e\)这条边的最小答案,那么我们有:\(g_{e,i}=w_e+\sum\limits_{j=1}^tf_{v_e,i+j}p_{e,j},f_{u,i}=\min\limits_{u_e=u}g_{e,i}\)。
那么我们可以把\(p\)翻转一下,右边的就变成了一个卷积的形式。
注意到只有后面的时间会对前面的时间有影响,所以我们对时间分治,先计算右边,然后计算右边对左边的影响,再计算左边即可。
边界的计算需要预处理一些最短路、前缀和之类的东西。
#include
#include
#include
#include
#include
using ld=double;
const int N=20007,M=107,S=57,T=65537;const ld pi=acos(-1);
namespace IO
{
char ibuf[(1<<21)+1],*iS,*iT;
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
}
using IO::read;
struct complex{ld x,y;}A[T],B[T],w[T];
complex operator+(const complex&a,const complex&b){return {a.x+b.x,a.y+b.y};}
complex operator-(const complex&a,const complex&b){return {a.x-b.x,a.y-b.y};}
complex operator*(const complex&a,const complex&b){return {a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};}
struct edge{int u,v,w;ld p[N],g[N],s[N];}e[M];
int n,m,t,x,lim,vis[S],dis[S],rev[T];ld f[S][N];
void init(int n)
{
int len=32-__builtin_clz(n);lim=1<>1]>>1)|((i&1)<<(len-1));
for(int i=0;i>1;
solve(mid+1,r),init(r-l+r-mid);
for(int j=1;j<=m;++j)
{
memset(A,0,lim<<5),memset(B,0,lim<<5);
for(int i=1;i<=r-l;++i) A[r-l-i].x=e[j].p[i];
for(int i=mid+1;i<=r;++i) B[i-mid-1].x=f[e[j].v][i];
FFT(A,1),FFT(B,1);
for(int i=0;i