CF553E Kyoya and Train
CF553E Kyoya and Train
分治\(FFT\)
先考虑\(dp\)转移,由于从起点出发无法判断一条边的走向,我们考虑从终点转移到起点。
设\(dp_{u,t}\)表示在\(t\)时刻到达点\(u\)的最小花费(这里的花费指到达终点的花费,我们所求的答案即为\(dp_{1,0}\))。
枚举出边\(e(u \rightarrow v)\)。
\[dp_{u,t}=\min c_e+\sum_{i=1}^T p_{e,j} dp_{v,t+i}
\]
直接转移必然\(TLE\),我们观察后面的式子,非常像卷积形式。
那么对于每条边,我们计算出\(g_{e,t}=\sum_{i=1}^T p_{e,i} dp_{v,t+i}\),然后用它去更新\(dp\)值即可。
因此我们考虑卷积,不过我们发现,\(g_{e,t}\)依赖于时间比它更后的\(dp\)值,而\(dp\)值可以用所有出边的\(g\)值计算贡献,所以利用分治\(FFT\)解决。
我们用\([mid+1,r]\)来更新\([l,mid]\)的\(g\)值。
\[g_{e,t}=\sum_{i=1}^T p_{e,i} dp_{v,t+i}
\]
我们考虑增量:
\[{\Delta_g}_{e,t}=\sum_{i=mid+1}^r p_{e,i-t} dp_{v,i}
\]
令\(a_i=p_{e,i+1},b_i=dp_{v,r-i}\)。
\[{\Delta_g}_{e,t}=\sum_{i=0}^{r-mid-1} a_{mid+i-t} b_{r-mid-1-i}
\]
一开始我们只需要预处理\([T,2T)\)的\(dp\)值即可。
时间复杂度:\(O(mT \log^2 T)\)。
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 55
#define M 105
#define T 20005
#define FT 131075
#define D double
using namespace std;
const int INF=1000000007;
const D pi=acos(-1.0);
struct virt
{
D x,y;
virt (D xx=0.0,D yy=0.0)
{
x=xx,y=yy;
}
virt operator + (virt a)
{
return virt(x+a.x,y+a.y);
}
virt operator - (virt a)
{
return virt(x-a.x,y-a.y);
}
virt operator * (virt a)
{
return virt(x*a.x-y*a.y,x*a.y+y*a.x);
}
}G[2][25],a[FT],b[FT];
int s,l,rev[FT];
void Pre()
{
for (int i=22;i;--i)
{
G[0][i]=virt(cos(pi/(1 << i-1)),sin(pi/(1 << i-1)));
G[1][i]=virt(G[0][i].x,-G[0][i].y);
}
}
void solve(int n)
{
s=1,l=0;
while (s<n)
s <<=1,++l;
for (int i=0;i<s;++i)
rev[i]=(rev[i >> 1] >> 1) | ((i & 1) << l-1);
}
void FFT(virt *a,int t)
{
for (int i=0;i<s;++i)
if (i<rev[i])
swap(a[i],a[rev[i]]);
for (int mid=1,o=1;mid<s;mid <<=1,++o)
for (int j=0;j<s;j+=mid << 1)
{
virt g(1.0,0.0);
for (int k=0;k<mid;++k,g=g*G[t][o])
{
virt x=a[j+k],y=g*a[j+k+mid];
a[j+k]=x+y,a[j+k+mid]=x-y;
}
}
}
int n,m,t,x,xx;
int mc[N][N];
D dp[N][T << 1],g[M][T << 1];
struct edge
{
int x,y,cost;
D t[T << 1];
D& operator [] (int x)
{
return t[x];
}
}e[M];
template<typename orz>
void ckmin(orz &x,orz y)
{
x=(x<y)?x:y;
}
void cdq(int l,int r)
{
if (l==r)
{
for (int i=1;i<=m;++i)
if (e[i].x!=n)
ckmin(dp[e[i].x][l],e[i].cost+g[i][l]);
return;
}
int mid=(l+r) >> 1;
if (r<t)
cdq(mid+1,r);
int Len1=r-l,Len2=r-mid;
for (int i=1;i<=m;++i)
if (e[i].x!=n)
{
int u=e[i].x,v=e[i].y;
for (int j=0;j<Len1;++j)
a[j]=virt(e[i][j+1],0.0);
for (int j=0;j<Len2;++j)
b[j]=virt(dp[v][r-j],0.0);
solve(Len1+Len2);
FFT(a,0),FFT(b,0);
for (int j=0;j<s;++j)
a[j]=a[j]*b[j];
FFT(a,1);
for (int j=l;j<=mid;++j)
g[i][j]+=a[r-j-1].x/s;
for (int j=0;j<s;++j)
a[j]=b[j]=virt();
}
cdq(l,mid);
}
int main()
{
Pre();
scanf("%d%d%d%d",&n,&m,&t,&x);
for (int i=0;i<=n;++i)
for (int j=0;j<=n;++j)
if (i==j)
mc[i][j]=0; else
mc[i][j]=INF;
for (int i=1;i<=m;++i)
{
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].cost);
ckmin(mc[e[i].x][e[i].y],e[i].cost);
for (int j=1;j<=t;++j)
scanf("%d",&xx),e[i][j]=(D)xx/100000;
}
for (int k=1;k<=n;++k)
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
ckmin(mc[i][j],mc[i][k]+mc[k][j]);
for (int i=1;i<=n;++i)
for (int j=0;j<(t << 1);++j)
dp[i][j]=191919191919191919.1919191919;
for (int i=0;i<(t << 1);++i)
dp[n][i]=(i<=t)?0:x;
for (int i=1;i<n;++i)
for (int j=t;j<(t << 1);++j)
dp[i][j]=mc[i][n]+x;
cdq(0,(t << 1)-1);
printf("%.12lf\n",dp[1][0]);
return 0;
}