CF553E Kyoya and Train
一、题目
二、解法
设 \(dp[i][j]\) 表示 \(j\) 时间走到 \(i\) 需要花费的最小期望钱数。
区别于随机游走问题,对于某状态 \(dp[u][j]\),需要决策一个后继节点 \(v\):
\[dp[u][j]=\min_v(\sum_k dp[v][j+k]\times p_{u,k})+c_i
\]
末状态 \(dp[u][i]=dis(u,n)+x,i>t\);\(dp[n][i]=0,i\leq t\)
时间复杂度 \(O(mt^2)\)
考虑优化,看到 \(\min\) 里面的求和你可能会想到 \(\tt FFT\),但是不能直接一发差卷积搞上去,因为本题转移顺序最外层是时间,如果你差卷积是默认知道 \(dp[v]\) 的所有状态的。所以我们要对时间分治,写一个分治 \(\tt FFT\)
我们把右边的 \(f[v[i]]\) 转移到一个 \(g[i]\) 中,分治到底层的时候再用 \(g[i]\) 去更新 \(f[u[i]]\),然后这道题给 \(\tt FFT\) 配系数时把我整傻了,还是要推柿子更有助于理解:
\[g_x=\sum_{i=mid+1}^rp_{i-x}f_i
\]
设 \(a_i=p_{i+1},b_i=f_{r-i}\),那么把求和柿子改写一下:
\[g_x=\sum_{i=0}^{r-mid-1} a_{i-x+mid}b_{r-i-mid-1}
\]
设卷出来的数组是 \(c\),那么让 \(g_x=c_{r-x-1}\) 即可,时间复杂度 \(O(t\log t\cdot m)\)
三、总结
优化转移的时候不仅要看结构,还要看顺序,特别是图问题中,保证转移顺序是优化的前提。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
const int M = 105;
const int N = 80005;
#define db double
const db pi = acos(-1.0);
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,t,x,u[M],v[M],c[M],d[M][M],rev[N];
db f[M][N],g[M][N],p[M][N];
struct comp
{
db x,y;
comp(db X=0,db Y=0) : x(X) , y(Y) {}
comp operator + (const comp &r) const {return comp(x+r.x,y+r.y);}
comp operator - (const comp &r) const {return comp(x-r.x,y-r.y);}
comp operator * (const comp &r) const {return comp(x*r.x-y*r.y,x*r.y+y*r.x);}
}A[N],B[N],C[N];
void FFT(comp *a,int len,int op)
{
for(int i=0;i<len;i++)
{
rev[i]=(rev[i>>1]>>1)|((i&1)*(len/2));
if(i<rev[i]) swap(a[i],a[rev[i]]);
}
for(int s=2;s<=len;s<<=1)
{
comp w=comp(cos(2*pi/s),sin(2*pi/s)*op);
for(int i=0,t=s/2;i<len;i+=s)
{
comp x=comp(1,0);
for(int j=0;j<t;j++,x=x*w)
{
comp fe=a[i+j],fo=a[i+j+t];
a[i+j]=fe+x*fo;
a[i+j+t]=fe-x*fo;
}
}
}
}
void cdq(int l,int r)
{
if(l==r)
{
for(int i=1;i<n;i++)
f[i][l]=1e9;
for(int i=1;i<=m;i++)
f[u[i]][l]=min(f[u[i]][l],g[i][l]);
return ;
}
int mid=(l+r)>>1;
cdq(mid+1,r);
for(int i=1;i<=m;i++)
{
int ln=r-l,lm=r-mid,len=1;
while(len<ln+lm) len<<=1;
for(int j=0;j<len;j++) A[j]=B[j]=C[j]=comp(0,0);
for(int j=0;j<ln;j++) A[j]=comp(p[i][j+1],0);
for(int j=0;j<lm;j++) B[j]=comp(f[v[i]][r-j],0);
FFT(A,len,1);FFT(B,len,1);
for(int j=0;j<len;j++) C[j]=A[j]*B[j];
FFT(C,len,-1);
for(int j=l;j<=mid;j++)
g[i][j]+=C[r-j-1].x/len;
}
cdq(l,mid);
}
signed main()
{
n=read();m=read();t=read();x=read();
memset(d,0x3f,sizeof d);
for(int i=1;i<=m;i++)
{
u[i]=read();v[i]=read();c[i]=read();
d[u[i]][v[i]]=c[i];
for(int j=1;j<=t;j++) p[i][j]=read()/1e5;
}
for(int i=1;i<=n;i++) d[i][i]=0;//attention
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=1;i<=m;i++)
{
db s=0;
for(int j=0;j<=t;s+=p[i][t-j],j++)
g[i][j]=s*(d[v[i]][n]+x)+c[i];
}
cdq(0,t);
printf("%.7f\n",min(f[1][0],0.0+x+d[1][n]));
}