题意
n个火车站,m条单向铁路,你要从1到n。每条铁路有一个费用,然后通过的时间是[1,t]中的,会给出通过某条边用每个时间的概率。如果你到n的时间大于t,就会被罚x。当你到达一个火车站的时候,你可以根据当前的情况改变方案。问从1到N的最小期望花费
n<=50,m<=100,t<=20000
题解
可以先有一个dp,d[i][j]表示从i出发,当前为j时刻时的最小花费
d
(
u
,
t
)
=
m
i
n
(
w
e
+
∑
i
=
0
T
p
[
e
]
[
i
]
d
(
v
,
t
+
i
)
)
d(u,t)=min(w_e+\sum_{i=0}^Tp[e][i]d(v,t+i))
d(u,t)=min(we+i=0∑Tp[e][i]d(v,t+i))
当t>T时,我们可以先预处理出费用的最短路(floyd),
如果迟到了就不用赶时间啦
那么现在的问题就是上面那个dp怎么优化转移
令
f
(
e
,
t
)
=
∑
i
=
0
T
p
[
e
]
[
i
]
d
(
v
,
t
+
i
)
f(e,t)=\sum_{i=0}^Tp[e][i]d(v,t+i)
f(e,t)=i=0∑Tp[e][i]d(v,t+i)
那么
d
(
u
,
t
)
=
m
i
n
(
w
e
+
f
(
e
,
t
)
)
d(u,t)=min(w_e+f(e,t))
d(u,t)=min(we+f(e,t))
观察f那一串,感觉很像卷积的形式
稍微改造一下,
f
(
e
,
n
−
t
)
=
∑
i
=
0
T
p
[
e
]
[
i
]
d
(
v
,
n
−
(
t
+
i
)
)
f(e,n-t)=\sum_{i=0}^Tp[e][i]d(v,n-(t+i))
f(e,n−t)=i=0∑Tp[e][i]d(v,n−(t+i))就是一个更正常的卷积了
但是问题是,我们并不是知道了所有的值,而是在过程中就需要用到这些值
CDQ分治
由于这个是先得到右边的,所以我们先递归处理右边,再FFT计算贡献,再递归处理另一边
然后l==r就遍历一下所有边对当前时间求出最小值
就相当于是一个f和d互相更新的结果
必须手写复数类…
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=102,T=40005;
const double INF=1e18;
#define pi acos(-1.0)
struct cpd{
double x,y;
cpd(){}
cpd(double _x,double _y):x(_x),y(_y){}
}A[T*2],B[T*2];
cpd operator +(cpd a,cpd b){
return cpd(a.x+b.x,a.y+b.y);
}
cpd operator -(cpd a,cpd b){
return cpd(a.x-b.x,a.y-b.y);
}
cpd operator *(cpd a,cpd b){
return cpd(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}
cpd operator /(cpd a,double p){
return cpd(a.x/p,a.y/p);
}
void FFT(cpd *p,int n,int dir){
for(int i=1,j=0;i<n-1;i++){
for(int s=n;j^=s>>=1,~j&s;) ;
if(i<j){
swap(p[i],p[j]);
}
}
for(int d=0;(1<<d)<n;d++){
int m=1<<d,m2=m*2;
double p0=pi/m*dir;
cpd wn(cos(p0),sin(p0));
for(int i=0;i<n;i+=m2){
cpd w(1,0);
for(int j=0;j<m;j++){
cpd t0=p[i+j],t1=p[i+j+m];
p[i+j]=t0+t1*w;
p[i+j+m]=t0-t1*w;
w=w*wn;
}
}
}
if(dir==-1)
for(int i=0;i<n;i++)
p[i].x=p[i].x/n;
}
struct node{
int u,v;
}e[N*2];
int dist[N][N],edge[N][N];
int n,m,t,fine;
double p[N][T];
double f[N][T];
double d[N][T];
void CDQ(int l,int r){
if(l==r){
for(int i=1;i<=n-1;i++)
d[i][l]=INF;
for(int i=1;i<=m;i++){
int u=e[i].u,v=e[i].v;
d[u][l]=min(d[u][l],f[i][l]+edge[u][v]);
//printf("%f\n",d[u][l]);
}
return ;
}
int mid=(l+r)>>1;
CDQ(mid+1,r);
for(int i=1;i<=m;i++){
int u=e[i].u,v=e[i].v;
int len=1;
while(len<=(r-l+1))
//while(len<=2*(r-l+1))
len<<=1;
for(int j=0;j<len;j++)
A[j]=B[j]=cpd(0,0);
for(int j=mid+1;j<=r;j++)
A[r-j].x=d[v][j];
//printf("A:%f %d\n",d[v][i],i);
for(int j=0;j<=r-l;j++)
B[j].x=p[i][j];
//printf("B:%f\n",d[v][i]);
FFT(A,len,1);
FFT(B,len,1);
for(int j=0;j<len;j++)
A[j]=A[j]*B[j];
FFT(A,len,-1);
for(int j=l;j<=mid;j++)
f[i][j]+=A[r-j].x;
}
CDQ(l,mid);
}
void Read(int &x) {
x=0;
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
while(c>='0'&&c<='9')
x=x*10+c-'0',c=getchar();
}
int main()
{
//freopen("1.in","r",stdin);
//scanf("%d%d%d%d",&n,&m,&t,&fine);
Read(n),Read(m),Read(t),Read(fine);
memset(dist,0x3f,sizeof dist);
for(int i=1;i<=n;i++)
dist[i][i]=0;
for(int i=1;i<=m;i++){
int u,v,w;
//scanf("%d%d%d",&u,&v,&w);
Read(u),Read(v),Read(w);
e[i].u=u,e[i].v=v;
edge[u][v]=w;
dist[u][v]=w;
for(int j=1;j<=t;j++){
int val;
//scanf("%d",&val);
Read(val);
p[i][j]=val*0.00001;
}
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
for(int i=1;i<=m;i++){
int u=e[i].u,v=e[i].v;
double z=1;
for(int k=t;k>=0;k--){
z-=p[i][t-k];
f[i][k]=z*(dist[v][n]+fine);
//printf("%f\n",f[u][v][k]);
}
}
CDQ(0,t);
printf("%.9f\n",d[1][0]);
}