Loading

CF553E Kyoya and Train

CF553E Kyoya and Train

还是那句话,我觉得分治FFT是有实现难度的 初学,dalao别D

看完题第一反应应该是设 \(dp(i,j)\) 表示时刻 \(j\) 到达点 \(i\) 的最小代价。

发现完全不会处理边界,想了一会就舍掉了。

\(dp(i,j)\) 表示时刻 \(j\)\(i\) 到达 \(n\) 的最小代价,那么答案就是 \(dp(1,0)\)

这个是可以转移的。

\[f(u,j)= \begin{cases} (u=n) \begin{cases} 0(j\le t)\\ x(j\gt t)\\ \end{cases} \\ (else) \begin{cases} \min\{\sum_{k=1}^{t} P_{(u,v),k}f(v,j+k)+w(u,v) \} (j\le t)\\ dis(u,n)+x(j\gt t)\\ \end{cases} \end{cases} \]

暴力是 \(O(mt^2)\) 的,倒序枚举 \(t\) 即可。

然后就不会了。。。

嗯,好,这个式子居然可以直接算出来,我只能爬,多项式nb。

首先应当注意到 \(\sum_{k=1}^{t} P_{(u,v),k}f(v,j+k)\) 是可以卷的,随便把一个多项式翻转就能卷了。

问题在于我们必须知道所有 \(j>i\)\(f(v,j)\) 才能计算 \(f(u,i)\) ,如果暴力倒序枚举 \(j\) 复杂度又回去了。

考虑分治FFT,这东西可以自己更新自己。应该先分治右区间再分治左区间。

然而还是不会

为啥不会算呢?仔细一想主要是复杂度不对,因为取 \(\min\) ,每次必须把长度为 \(t\) 的多项式卷起来。

然后我怎么就没想到搞一个可以直接加的东西出来哦/kk

\(g(i,j)\) 表示 在 \(j\) 时刻开始走第 \(i\) 条边走到 \(n\) 的最短时间,那么 \(f(a_i,j)=g(i,j)\)

这个 \(g(i,[l,mid])\) 可以通过 \(f(b_i,[mid+1,r])\) 搞出来, 而且因为后半段的 \(f\) 已经确定,必然最优,直接相加没有错!

递归到 \(l=r\) 的时候用 \(g\)\(f\) 更新掉就好了

转移大概长这样:

(钦定 \(P_{i,0}=0\)看着舒服)

\[g(i,x)=\sum_{j=0}^{t}P_{i,j}f(b_i,x+j) \]

用cdq分治的思路,不断统计右区间对左区间的贡献就能算出 \(g\) 了。

然后就是非常烦的边界处理,一定想清楚再打,尽量不要调。但是我边界对了FFT打挂了是我没想到的

\(A_j=P_{i,j}(l\le j\le r),B_{r-j+mid+1}=f(b_i,r-j)(mid+1\le r-j\le r)\)

\(A,B\) 卷起来看看是啥

\([z^k]A*B=\sum_{j=0}^{k}A_jB_{k-j}=\sum _{j=0}^{k}P_{i,j}f(b_i,r-k+j)\)

所以提取 \(r-j\) 这一项加到 \(j\in [l,mid]\) 上即可。

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mkp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define sz(v) (int)v.size()
typedef long long LL;
typedef double db;
template<class T>bool ckmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>bool ckmin(T&x,T y){return x>y?x=y,1:0;}
#define rep(i,x,y) for(int i=x,i##end=y;i<=i##end;++i)
#define per(i,x,y) for(int i=x,i##end=y;i>=i##end;--i)
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return f?x:-x;
}
const int V=55;
const int E=105;
const int N=20005;
const int M=N<<2;
const db inf=1e15;
int n,m,t,X,a[E],b[E],c[E];
db p[E][N<<1],dis[V][V],f[E][N<<1];

namespace poly{

const db PI=acos(-1.0);

int rev[M],lg,lim;
db g[E][N<<1];
void init(const int&n){
	for(lg=0,lim=1;lim<=n;++lg,lim<<=1);
	for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
}
struct cp{
	db x,y;
	cp(){x=y=0;}
	cp(db x_,db y_){x=x_,y=y_;}
	cp operator + (const cp&t)const{return cp(x+t.x,y+t.y);}
	cp operator - (const cp&t)const{return cp(x-t.x,y-t.y);}
	cp operator * (const cp&t)const{return cp(x*t.x-y*t.y,x*t.y+y*t.x);}
};
void FFT(cp*a,int op){
	for(int i=0;i<lim;++i)
		if(i>rev[i])swap(a[i],a[rev[i]]);
	for(int i=1;i<lim;i<<=1){
		cp wn=cp(cos(PI/i),sin(PI/i)*(op?1:-1));
		for(int j=0;j<lim;j+=i<<1){
			cp w0=cp(1,0);
			for(int k=0;k<i;++k,w0=w0*wn){
				const cp X=a[j+k],Y=w0*a[i+j+k];
				a[j+k]=X+Y,a[i+j+k]=X-Y;
			}
		}
	}
	if(op)return;
	for(int i=0;i<lim;++i)a[i].x/=lim;
}
void CDQ_FFT(int l,int r){
	if(l==t&&r==2*t-1)return;
	static cp A[M],B[M];
	if(l==r){
		rep(i,1,n-1)f[i][l]=inf;
		for(int i=1;i<=m;++i)if(a[i]!=n)ckmin(f[a[i]][l],g[i][l]+c[i]);
		return;
	}
	int mid=(l+r)>>1;
	CDQ_FFT(mid+1,r);
	init(r-l+r-mid);
	for(int i=1;i<=m;++i){
		if(a[i]==n)continue;
		for(int j=0;j<lim;++j)A[j]=B[j]=cp(0,0);
		for(int j=0;j<=r-l;++j)A[j].x=p[i][j];
		for(int j=mid+1;j<=r;++j)B[j-mid-1].x=f[b[i]][r-j+mid+1];
		FFT(A,1),FFT(B,1);
		for(int j=0;j<lim;++j)A[j]=A[j]*B[j];
		FFT(A,0);
		for(int j=l;j<=mid;++j)g[i][j]+=A[r-j].x;
	}
	CDQ_FFT(l,mid);
}
}
signed main(){
	n=read(),m=read(),t=read(),X=read();
	rep(i,1,n)rep(j,1,n)dis[i][j]=i==j?0:inf;
	for(int i=1;i<=m;++i){
		a[i]=read(),b[i]=read(),c[i]=dis[a[i]][b[i]]=read();
		rep(j,1,t)p[i][j]=1.*read()/100000;
	}
	rep(k,1,n)rep(i,1,n)rep(j,1,n)ckmin(dis[i][j],dis[i][k]+dis[k][j]);
	rep(i,1,n-1)rep(j,t,t*2-1)f[i][j]=dis[i][n]+X;
	rep(i,0,t*2-1)f[n][i]=i<=t?0:X;
	poly::CDQ_FFT(0,t*2-1);
	printf("%.9lf\n",f[1][0]);
	return 0;
}

posted @ 2020-12-30 09:03  zzctommy  阅读(73)  评论(0编辑  收藏  举报