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]));
}
posted @ 2021-07-20 22:19  C202044zxy  阅读(167)  评论(0编辑  收藏  举报