题解 CF553E 【Kyoya and Train】
xtx1092515503
2020-08-16 17:37:03
~~这道题可以用普通FFT就过掉,没必要用分治FFT呀~~
我们一个naive的思路是设 $f[i][j]$ 表示你最少要花多少钱才能在时刻 $j$ 到达位置 $i$。但是这个边界条件没有定义——你咋知道 $f[1][k](k>0)$ 要花多少钱呢?按理说应该是 $\infty$,但是好像又不太对劲……
所以说我们仔细一想会发现这个状态是不行滴(~~我一开始就在这种思路上纠结了一晚上然后成功崩溃看了题解~~)。正难则反,我们考虑反着来,即设 $f[i][j]$ 表示当前是 $j$ 时刻在位置 $i$,最少还要花多少钱才能到达位置 $n$。
我们设 $DIS_i$ 表示位置 $i$ 到位置 $n$ 最少要花多少钱(不考虑通过时间),则显然这个可以通过一遍Dijkstra求出。则对于 $f[i][j](j>t)$,我们必有 $f[i][j]=DIS_i+x$($x$ 是罚款),因为反正已经超时了,那就走最划算的路吧。
则我们考虑对于一条有向边 $(x,y,z)$,则 $x$ 可以从 $y$ 转移过来。设当前边的概率数组是 $prob$,则有
$$f[x][k]\leftarrow z+\sum\limits_{j=1}^tprob_j\times f[y][j+k]$$
其中 $\leftarrow$ 表示可以被转移,且有 $k\in[0,t]$。
我们考虑翻转 $prob$ 数组。则翻转后,转移式就变成了
$$f[x][k]\leftarrow z+\sum\limits_{j=1}^tprob_{t-j+1}\times f[y][j+k]$$
发现后面是个卷积,两项的下标和恒为 $t+k+1$,于是
$$f[x][k]\leftarrow z+(prob\times f[y])_{t+k+1}$$
然后就可以直接FFT转移辣。
然后因为是在图上转移,而如果用Dijkstra转移的话 $f[x]$ 之间又没有明确的大小关系,故考虑直接SPFA(当然SPFA已经死了,但就算它死了复杂度仍然没问题)。则复杂度为 $O(nmt\log t)$,没有卡常就排到了首页靠前的位置。
代码:
```cpp
#include<bits/stdc++.h>
using namespace std;
const double pi=acos(-1);
const double eps=1e-10;
const int N=200100;
int n,m,t,pay,head[60],DIS[60];
struct edge{
int to,next,val;
}edge[110];
void ae(int u,int v,int w,int p){
edge[p].next=head[u],edge[p].to=v,edge[p].val=w,head[u]=p;
}
double prob[110][N],dis[60][N],now[N];
int rev[N],lim=1,LG;
struct cp{
double x,y;
cp(double X=0,double Y=0){
x=X,y=Y;
}
friend cp operator +(const cp &u,const cp &v){
return cp(u.x+v.x,u.y+v.y);
}
friend cp operator -(const cp &u,const cp &v){
return cp(u.x-v.x,u.y-v.y);
}
friend cp operator *(const cp &u,const cp &v){
return cp(u.x*v.x-u.y*v.y,u.x*v.y+u.y*v.x);
}
}A[N],B[N];
void FFT(cp *a,int tp){
for(int i=0;i<lim;i++)if(rev[i]>i)swap(a[rev[i]],a[i]);
for(int md=1;md<lim;md<<=1){
cp rt=cp(cos(pi/md),tp*sin(pi/md));
for(int stp=md<<1,pos=0;pos<lim;pos+=stp){
cp w=cp(1,0);
for(int i=0;i<md;i++,w=w*rt){
cp x=a[pos+i],y=a[pos+md+i]*w;
a[pos+i]=x+y;
a[pos+md+i]=x-y;
}
}
}
if(tp==-1)for(int i=0;i<lim;i++)a[i].x/=lim;
}
void mul(double *a,double *b,double *c){
for(int i=0;i<lim;i++)A[i]=B[i]=cp(0,0);
for(int i=0;i<(lim>>1);i++)A[i]=cp(a[i],0),B[i]=cp(b[i],0);
FFT(A,1),FFT(B,1);
for(int i=0;i<lim;i++)A[i]=A[i]*B[i];
FFT(A,-1);
for(int i=0;i<lim;i++)c[i]=A[i].x;
}
bool vis[60];
void Dijkstra(){
priority_queue<pair<int,int> >q;
for(int i=1;i<=n;i++)DIS[i]=0x3f3f3f3f;
DIS[n]=0,q.push(make_pair(0,n));
while(!q.empty()){
int x=q.top().second;q.pop();
if(vis[x])continue;vis[x]=true;
for(int i=head[x],y;i!=-1;i=edge[i].next){
y=edge[i].to;
if(DIS[y]>DIS[x]+edge[i].val)DIS[y]=DIS[x]+edge[i].val,q.push(make_pair(-DIS[y],y));
}
}
}
bool in[60];
void SPFA(){
queue<int>q;
for(int i=1;i<=n;i++){
for(int j=0;j<=t;j++)dis[i][j]=0x3f3f3f3f;
for(int j=t+1;j<=2*t;j++)dis[i][j]=DIS[i]+pay;
}
for(int j=0;j<=t;j++)dis[n][j]=0;
q.push(n),in[n]=true;
while(!q.empty()){
int x=q.front();q.pop(),in[x]=false;
for(int i=head[x],y;i!=-1;i=edge[i].next){
y=edge[i].to,mul(dis[x],prob[i],now);
bool modi=false;
for(int j=0;j<=t;j++)if(dis[y][j]>now[t+1+j]+edge[i].val)modi=true,dis[y][j]=now[t+1+j]+edge[i].val;
if(modi&&!in[y])in[y]=true,q.push(edge[i].to);
}
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&t,&pay),memset(head,-1,sizeof(head));
for(int i=1,x,y,z;i<=m;i++){
scanf("%d%d%d",&x,&y,&z),ae(y,x,z,i);
for(int j=1;j<=t;j++)scanf("%d",&x),prob[i][j]=1.0*x/100000;
reverse(prob[i]+1,prob[i]+t+1);
}
while(lim<=(t<<2))lim<<=1,LG++;
for(int i=0;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(LG-1));
Dijkstra();
SPFA();
printf("%.10lf\n",dis[1][0]);
return 0;
}
```