[Codeforces 553E]Kyoya and Train(期望DP+Floyd+分治FFT)

题面

给出一个\(n\)个点\(m\)条边的有向图(可能有环),走每条边需要支付一个价格\(c_i\),需要的时间为\([1,T]\)中随机的整数,时间为\(j\)的概率为\(p_{i,j}\)。从\(1\)出发走到\(n\),如果到\(n\)的时间超过\(T\),就需要再支付\(X\)。找出一条路径,使得支付钱数的期望值最小。输出最小期望。

\(n \leq 50,m \leq 100,T \leq 20000\)

分析

设\(dp[x][t]\)表示当前走到了节点为\(x\),已经经过的时间为\(t\)时,从\(x\)到终点的最小花费。那么我们可以枚举从\(x\)出发的每条边\(i:x \to y\),显然可以写出下面的转移方程

\[dp[x][t] = \min( c[i]+\sum_{k=1}^Tdp[y][t+k] \times p[i][k])
\]

注意到把\(dp[y]\)反转成\(dp'[y]\)后,乘积可以化为\(\sum_{k=1}^Tdp'[y][2T-(t+k)] \times p[i][k])\),这是一个卷积的形式,可以用分治FFT优化

由于图上有环,按照点的顺序转移是有后效性的。但是如果我们按照时间的顺序分治,先计算出所有点中\(t\)的范围在\([mid+1,r]\)中的\(dp\)值,用FFT计算出它们的代价\(c[i]+\sum_{k=1}^Tdp[y][t+k] \times p[i][k]\),用这个代价来更新\([l,mid]\)的dp值,就可以避免后效性。因为时间小的状态一定由时间大的状态转移过来。

初始值:

\[dp[i][j]= \begin{cases} 0,i=n,j\in[1,T] \\ + \infty,i \in[1,n-1],j \in[1,T] \\ X+mincost[i][n] ,j \in[T+1,2T]\end{cases}
\]

最后一种情况中\(mincost[i][n]\)表示\(i\)到\(n\)的最小代价,可以用Floyd预处理。这里实际上用了贪心的思想,既然现在已经迟到了,那之后无论走多快都会被罚\(X\)。不如选价格最小的路慢慢走。由于所有\([T+1,2T]\)的dp值已经求出,可以做一次FFT计算代价,然后直接对\([0,T]\)做分治。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 50
#define maxm 100
#define maxt 20000
#define INF 1e20
const double pi=acos(-1.0);
using namespace std;
struct com {
double real;
double imag;
com() { }
com(double _real,double _imag) {
real=_real;
imag=_imag;
}
com(double x) {
real=x;
imag=0;
}
void operator = (const com x) {
this->real=x.real;
this->imag=x.imag;
}
void operator = (const double x) {
this->real=x;
this->imag=0;
}
friend com operator + (com p,com q) {
return com(p.real+q.real,p.imag+q.imag);
}
friend com operator + (com p,double q) {
return com(p.real+q,p.imag);
}
void operator += (com q) {
*this=*this+q;
}
void operator += (double q) {
*this=*this+q;
}
friend com operator - (com p,com q) {
return com(p.real-q.real,p.imag-q.imag);
}
friend com operator - (com p,double q) {
return com(p.real-q,p.imag);
}
void operator -= (com q) {
*this=*this-q;
}
void operator -= (double q) {
*this=*this-q;
}
friend com operator * (com p,com q) {
return com(p.real*q.real-p.imag*q.imag,p.real*q.imag+p.imag*q.real);
}
friend com operator * (com p,double q) {
return com(p.real*q,p.imag*q);
}
void operator *= (com q) {
*this=(*this)*q;
}
void operator *= (double q) {
*this=(*this)*q;
}
friend com operator / (com p,double q) {
return com(p.real/q,p.imag/q);
}
void operator /= (double q) {
*this=(*this)/q;
}
void print() {
printf("%lf + %lf i ",real,imag);
}
}; void fft(com *x,int *rev,int n,int type) {//orz Fourier
for(int i=0; i<n; i++) if(i<rev[i]) swap(x[i],x[rev[i]]);
for(int len=1; len<n; len*=2) {
int sz=len*2;
com wn1=com(cos(2*pi/sz),type*sin(2*pi/sz));
for(int l=0; l<n; l+=sz) {
int r=l+len-1;
com wnk=1;
for(int i=l; i<=r; i++) {
com tmp=x[i+len];
x[i+len]=x[i]-wnk*tmp;
x[i]=x[i]+wnk*tmp;
wnk=wnk*wn1;
}
}
}
if(type==-1) for(int i=0; i<n; i++) x[i]/=n;
} int n,m,T,X;
struct edge {
int from;
int to;
int cost;
double p[maxt+5];
} E[maxm+5];
int dist[maxn+5][maxn+5];
void floyd() {
for(int k=1; k<=n; k++) {
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
}
}
}
} double sum[maxm+5][maxt*2+5];
double dp[maxn+5][maxt*2+5];
void calc(int l,int mid,int r) { //计算时间在[mid+1,r]的dp,对时间在[l,mid]的dp的贡献
static com a[maxt*10+5],b[maxt*10+5];
static int rev[maxt*10+5];
for(int e=1; e<=m; e++) {//枚举转移的边
int N=1,L=0;
int tlim=min(T,r-l+1);
while(N<r-mid+tlim) {
N*=2;
L++;
}
for(int i=0;i<N;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
for(int i=0;i<N;i++){
a[i]=0;
b[i]=0;
}
//计算[mid+1,r]转移时的代价,是卷积形式
for(int i=0;i<r-mid;i++){
a[i]=dp[E[e].to][r-i];//反转
}
for(int i=0;i<=tlim;i++){
b[i]=E[e].p[i];
}
fft(a,rev,N,1);
fft(b,rev,N,1);
for(int i=0;i<N;i++) a[i]*=b[i];
fft(a,rev,N,-1);
//把答案累计到[l,mid]上
for(int i=l;i<=mid;i++) sum[e][i]+=a[r-i].real;
}
}
void divide(int l,int r){//orz CDQ
if(l==r){
//所有需要的数已经计算完毕,可以转移了
for(int i=1;i<=m;i++){
dp[E[i].from][l]=min(dp[E[i].from][l],sum[i][l]+E[i].cost);
}
return;
}
int mid=(l+r)>>1;
divide(mid+1,r);
calc(l,mid,r);
divide(l,mid);
} int main(){
scanf("%d %d %d %d",&n,&m,&T,&X);
memset(dist,0x3f,sizeof(dist));
for(int i=1;i<=m;i++){
scanf("%d %d %d",&E[i].from,&E[i].to,&E[i].cost);
dist[E[i].from][E[i].to]=min(dist[E[i].from][E[i].to],E[i].cost);
for(int j=1;j<=T;j++){
scanf("%lf",&E[i].p[j]);
E[i].p[j]/=1e5;
}
}
for(int i=1;i<=n;i++) dist[i][i]=0;
floyd();
for(int i=1;i<=n;i++){
for(int j=0;j<=T;j++){
//初始化不超时的花费
if(i!=n) dp[i][j]=INF;
else dp[i][j]=0;
//如果超时,就随便选距离最小的去
dp[i][j+T]=X+dist[i][n];
}
}
calc(0,T,2*T+1);
divide(0,T);
printf("%.6f\n",dp[1][0]);
}

[Codeforces 553E]Kyoya and Train(期望DP+Floyd+分治FFT)的更多相关文章

  1. CodeForces 553E Kyoya and Train 动态规划 多项式 FFT 分治

    原文链接http://www.cnblogs.com/zhouzhendong/p/8847145.html 题目传送门 - CodeForces 553E 题意 一个有$n$个节点$m$条边的有向图 ...

  2. Codeforces 553E Kyoya and Train

    题目大意 链接:CF533E 给一张\(n\)个点,\(m\)条边的图,起点\(1\)终点\(n\),如果不能在\(T\)的时间内到达则需支付\(X\)的代价. 走每条边都会支付一定代价,经过一条边\ ...

  3. 【CF553E】Kyoya and Train 最短路+cdq分治+FFT

    [CF553E]Kyoya and Train 题意:有一张$n$个点到$m$条边的有向图,经过第i条边要花$c_i$元钱,经过第i条边有$p_{i,k}$的概率要耗时k分钟.你想从1走到n,但是如果 ...

  4. ●codeforces 553E Kyoya and Train

    题链: http://codeforces.com/problemset/problem/623/E 题解: FFT,DP 题意: 一个有向图,给出每条边的起点u,终点v,费用c,以及花费每种时间的概 ...

  5. [Codeforces 865C]Gotta Go Fast(期望dp+二分答案)

    [Codeforces 865C]Gotta Go Fast(期望dp+二分答案) 题面 一个游戏一共有n个关卡,对于第i关,用a[i]时间通过的概率为p[i],用b[i]通过的时间为1-p[i],每 ...

  6. 【XSY2666】排列问题 DP 容斥原理 分治FFT

    题目大意 有\(n\)种颜色的球,第\(i\)种有\(a_i\)个.设\(m=\sum a_i\).你要把这\(m\)个小球排成一排.有\(q\)个询问,每次给你一个\(x\),问你有多少种方案使得相 ...

  7. [题解] Atcoder ABC 213 H Stroll DP,分治FFT

    题目 令\(dp_{i,j}\)表示从点1到达点i,路径长度为j的方案数.转移为\(dp_{i,j}=\sum_{(i,v,w)\in E}dp_{v,j-w}p_{i,v,w}\). 显然只能从长度 ...

  8. 【bzoj4720】【noip2016】【换座位】期望dp+Floyd

    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62370736 wa...已经快一年了,重新来做这 ...

  9. CF 553E Kyoya and Train

    题目分析 期望\(\text{dp}\). 设\(f_{i,j}\)表示在第\(j\)个时刻从\(i\)点出发,到达终点的期望花费. 有转移方程: \[ f_{x,t}=\min_{(x,y)\in ...

随机推荐

  1. Python3基础——函数

    ython 函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也可 ...

  2. Apache HttpClient 4.5 在Springboot中使用

    ConnectionRequestTimeout httpclient使用连接池来管理连接,这个时间就是从连接池获取连接的超时时间,可以想象下数据库连接池 ConnectTimeout 连接建立时间, ...

  3. cocos creator屏幕适配的一些知识点

    一. cocos creator 提供的几种适配策略 EXACT_FIT: 整个应用程序在指定区域可见,无需尝试保留原始纵横比.可能会出现失真,应用程序会被拉伸或压缩.也就是说设计分辨率的长和宽不会等 ...

  4. CRF基础知识以及如何实现Learning,Inference

    CRF:Conditional Random Field,即条件随机场. 首先介绍一下基础背景知识.机器学习中的分类问题可以分为硬分类和软分类.硬分类常见的模型有SVM.PLA.LDA等.SVM可以称 ...

  5. Git的介绍以及安装

    Git的简单介绍 Git是一个开源的分布式版本控制系统,可以有效,高速的处理从很小到非常大的项目管理,GIT是为了帮助linux内核开发而开发的一个开放源码的版本控制软件 Git的安装 Linux平台 ...

  6. 【图论】HDU 5961 传递

    题目内容 题目链接 我们称一个有向图G是传递的当且仅当对任意三个不同的顶点a,若G中有 一条边从a到b且有一条边从b到c ,则G中同样有一条边从a到c. 我们称图G是一个竞赛图,当且仅当它是一个有向图 ...

  7. 学习ing

    1.从硬件和逻辑两个角度探讨什么是内存?硬件上看,内存就是电脑上的硬件--内存条.内存通过内存条不同的实现原谅分为DRAM(DRAM已经发展出好多代)和SRAM.从逻辑的角度来说,内存就是一个可以随机 ...

  8. Vagrant系列(一)----win10搭建Vagrant+VirtualBox环境_

      一.Vagrant是什么?     vagrant是一个操作虚拟机的工具.是一个基于Ruby的工具,用于创建和部署虚拟化开发环境.    通过命令和配置文件来管理虚拟机,很快就能完成一套开发环境的 ...

  9. appium_android-常见的问题

    po模型的原则: 用公共方法代表UI所提供的功能 方法应该返回其他的PageObject或者返回用于断言的数据 同样的行为不同的结果可以建模为不同的方法 不要在方法内加断言 字段意义 不要暴露页面内部 ...

  10. 面试官问:HashMap在并发情况下为什么造成死循环?一脸懵

    这个问题是在面试时常问的几个问题,一般在问这个问题之前会问Hashmap和HashTable的区别?面试者一般会回答:hashtable是线程安全的,hashmap是线程不安全的. 那么面试官就会紧接 ...