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)= egin{cases} (u=n) egin{cases} 0(jle t)\ x(jgt t)\ end{cases} \ (else) egin{cases} min{sum_{k=1}^{t} P_{(u,v),k}f(v,j+k)+w(u,v) } (jle t)\ dis(u,n)+x(jgt 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}(lle jle r),B_{r-j+mid+1}=f(b_i,r-j)(mid+1le r-jle 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) 这一项加到 (jin [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
",f[1][0]);
	return 0;
}

原文地址:https://www.cnblogs.com/zzctommy/p/14209728.html