http://codeforces.com/problemset/problem/553/E (题目链接)

艹尼玛,CF还卡劳资常数w(゚Д゚)w!!系统complex被卡TLE了T_T,劳资写了一天一夜啊!!博客都写了这么长啊!!

题意

  ${n}$个点${m}$条边的有向图,给出起点和终点,如果不能在${T}$时间内到达终点则需要付出一定的代价,走每条边需要付出给定的代价,每条边经过时间的分布列给定,最小化所需要的期望的代价。

Solution

  毛爷爷论文题。

  先考虑暴力${DP}$怎么做。${f_{x,t}}$表示到达${x}$点,花费时间${t}$所期望的最小代价。那么转移很显然,对于从${x}$出发的每一条边${e_i=(x,y_i)}$,设${P_{e_i,t}}$为走这条边经过时间${t}$的概率,那么转移方程:$${f_{x,t}=Min \{\sum_{j=1}^{T} {f_{y_i,t+j}*P_{e_i,j}}\}}$$

  刚开始一直不太理解这个${DP}$,题目中并没有保证给出的有向图是个${DAG}$,那这样的${DP}$不应该用高斯消元求解吗。其实可以这样理解,我们把图看做是按照时间分层的有向图,每一条边至少要花费${1}$的时间,那么一个点就不可能再回到它的祖先,除非它经过的边的时间为负数。那么整个图就构成了一个${DAG}$,就可以${DP}$了。于是状态${O(T*m)}$,转移${O(T)}$,复杂度${O(m*T*T)}$,GG,考虑怎么对这个${DP}$进行优化。对于每一条边${e}$,我们用${S_{e,t}}$表示上面这个转移方程的右边${S_{e,t}=\sum_{j=1}^{T} {f_{y_i,t+j}*P_{e_i,j}}}$。那么很显然,${f}$的转移方程就可以改为:$${f_{x.t}=Min \{S_{e_i,t}\}}$$

  这样做有什么用呢,我们对时间分治(当然,此时间非彼时间)。如果我们要求出所有${l<=t<=r}$的对应到所有边的${f_{x,t}}$和${S{e,t}}$,那么我们设${mid=\lceil\frac{l+r}{2}\rceil}$,先求出所有${mid<=t<=r}$的${f_{x,t}}$,然后再用这些${f_{x,t}}$去更新${l<=t<mid}$的${S_{e,t}}$,接下来递归求解${l<=r<mid}$的${f_{x,t}}$。如果${l=r}$那么直接用${S}$去更新${f}$。怎么用${f}$去更新${S}$呢,我们运用${FFT}$。$${S_{e,t}=\sum_{j=1}^{T} {f_{y_i,t+j}*P_{e_i,j}}}$$

  这个东西长的就像个卷积,我们考虑怎么将它化为卷积的模式。假设现在我们计算${mid<=t<=r}$,边${e}$中,${f_{x,t}}$对${S_{e,mid-2}}$的贡献,那么如图所示:

  这样的一个相乘的过程怎么转化为卷积呢,我们将${f_x}$倒过来,并在它的末尾添${0}$,就得到了卷积的形式:

  于是我们就可以构造多项式:$${A(t)=\sum_{i=0}^{r-mid+1}  {f_{x,r-i}×t^i}   +   \sum_{i=r-mid+2}{T-1}  {0×t^i}}$$

$${B(t)=\sum_{i=0}^{T-1}  {P_{e,i+1}×t^i}}$$

$${C=A×B}$$

  那么我们可以发现,${l<=t<mid}$中,${S_{e,t}}$得到的贡献就是卷积${c_{r-t-1}=\sum_{j=0}^{r-t-1}  {a_j×b_{r-t-1-j}}}$

细节

  感觉这道题细节还是蛮多的,脑袋晕晕的写了一天,都怪昨天睡太晚→_→。不如讨论一下一些需要注意的细节。

  第一,数组怎么开。${DP}$相关的数组的时间一维可能会很大,我们考虑当时间超过${T}$之后,具体的到达终点的时间已经对我们没有意义了,反正要罚钱,不如走一条买的票最便宜的路线到达终点。所以对于${f_{x,t}=dis(x,n)+X;t>T}$。考虑时间这一维要开大的数组,我们从一种状态${f_{x,t},t<=T}$,它最多可能转移到的状态的${t}$不会超过${2*T}$,因为一条路线最多耗费时间${T}$;而对于第二种状态${f_{x,t},t>T}$,我们可以很轻松的求出它,已经没有转移的必要了。于是乎,时间这一维的数组大小需要开到${2*T}$。

  第二,考虑分治的初始化。首先,对于${T<t<=2*T}$的${f}$,它已经不需要再求一遍了,所以我们直接分治${solve(0,T)}$。在分治求解之前,我们需要计算${T<t<=2*T}$的${f}$对之前的${S}$的贡献,所以先做一遍${FFT}$,再进行分治求解。

  第三,就是${FFT}$时下标之间的对应关系。

代码

// codeforces 553E
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<complex>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
#define LL long long
#define inf 1ll<<30
#define Pi acos(-1.0)
#define upd(x,y) if ((x)>(y)) (x)=(y)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
inline int gi() {
int x=0,f=1;char ch=getchar();
while (ch<'0' || ch>'9') {if (ch=='-') f=-1;ch=getchar();}
while (ch>='0' && ch<='9') {x=x*10+ch-'0';ch=getchar();}
return x*f;
} //typedef complex<double> E;
const int maxn=110,maxm=210,maxt=20010;
//E A[maxt<<2],B[maxt<<2];
int rev[maxt<<2],dis[maxn][maxn],n,m,T,X,N,L;
double f[maxn][maxt<<1],P[maxm][maxt<<1],S[maxm][maxt<<1]; struct edge {int u,v,w;}e[maxm];
struct E{ double x,y; }A[maxt<<2],B[maxt<<2];
inline E operator + (const E &a,const E &b) { return (E){a.x+b.x,a.y+b.y}; }
inline E operator - (const E &a,const E &b) { return (E){a.x-b.x,a.y-b.y}; }
inline E operator * (const E &a,const E &b) { return (E){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x}; } void FFT(E *a,int f) {
for (int i=0;i<N;i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
for (int i=1;i<N;i<<=1) {
E wn=(E){cos(Pi/i),f*sin(Pi/i)};
for (int p=i<<1,j=0;j<N;j+=p) {
E w=(E){1,0};
for (int k=0;k<i;k++,w=w*wn) {
E x=a[j+k],y=a[j+k+i]*w;
a[j+k]=x+y;a[j+k+i]=x-y;
}
}
}
}
void calculate(int l,int r,int mid) {
int len=r-l;
for (int j=1;j<=m;j++) {
L=-1;for (N=1;N<=len-1+r-mid;N<<=1) L++; //mdzz错误又犯了
for (int i=0;i<N;i++) rev[i]=(rev[i>>1]>>1) | ((i&1)<<L);
for (int i=0;i<N;i++) A[i]=B[i]=(E){0,0};
for (int i=0;i<r-mid+1;i++) A[i]=(E){f[e[j].v][r-i],0};
for (int i=0;i<len;i++) B[i]=(E){P[j][i+1],0};
FFT(A,1);FFT(B,1);
for (int i=0;i<N;i++) A[i]=A[i]*B[i];
FFT(A,-1);
for (int i=mid-1;i>=l;i--) S[j][i]+=A[r-i-1].x/N;
}
}
void solve(int l,int r) {
if (l==r) {
for (int i=1;i<=m;i++) f[e[i].u][l]=min(f[e[i].u][l],S[i][l]+e[i].w);
return;
}
int mid=(l+r)>>1;
solve(mid+1,r);
calculate(l,r,mid+1);
solve(l,mid);
} int main() {
n=gi(),m=gi(),T=gi(),X=gi();
for (int i=1;i<=n;i++) {
for (int j=1;j<=n;j++) dis[i][j]=inf;
dis[i][i]=0;
}
for (int i=1;i<=m;i++) {
e[i].u=gi(),e[i].v=gi(),e[i].w=gi();
dis[e[i].u][e[i].v]=min(dis[e[i].u][e[i].v],e[i].w);
for (int x,j=1;j<=T;j++) {
x=gi();
P[i][j]=(double)x/100000;
}
}
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=j && j!=k && i!=k && dis[i][k]!=inf && dis[k][j]!=inf)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
for (int i=0;i<=T;i++) f[n][i]=0;
for (int i=T+1;i<=2*T;i++) f[n][i]=X;
for (int i=1;i<n;i++)
for (int j=0;j<=2*T;j++) {
if (j>T) f[i][j]=dis[i][n]+X;
else f[i][j]=inf;
}
calculate(1,T*2,T+1);
solve(0,T);
printf("%.6lf\n",f[1][0]);
return 0;
}

【codeforces 553E】 Kyoya and Train的更多相关文章

  1. 【47.95%】【codeforces 554C】Kyoya and Colored Balls

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  2. 【76.83%】【codeforces 554A】Kyoya and Photobooks

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  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 415D】Mashmokh and ACM(普通dp)

    [codeforces 415D]Mashmokh and ACM 题意:美丽数列定义:对于数列中的每一个i都满足:arr[i+1]%arr[i]==0 输入n,k(1<=n,k<=200 ...

  5. 【50.00%】【codeforces 602C】The Two Routes

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  6. 【33.33%】【codeforces 586D】Phillip and Trains

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  7. 【codeforces 707E】Garlands

    [题目链接]:http://codeforces.com/contest/707/problem/E [题意] 给你一个n*m的方阵; 里面有k个联通块; 这k个联通块,每个连通块里面都是灯; 给你q ...

  8. 【codeforces 707C】Pythagorean Triples

    [题目链接]:http://codeforces.com/contest/707/problem/C [题意] 给你一个数字n; 问你这个数字是不是某个三角形的一条边; 如果是让你输出另外两条边的大小 ...

  9. 【codeforces 709D】Recover the String

    [题目链接]:http://codeforces.com/problemset/problem/709/D [题意] 给你一个序列; 给出01子列和10子列和00子列以及11子列的个数; 然后让你输出 ...

随机推荐

  1. 将jira添加至开机自启动

    东北证券网金部jira项目管理系统,经常莫名挂掉,于是乎将jira服务加入开机自启动. jira.sh脚本代码如下: #!/bin/sh # chkconfig: # description:jira ...

  2. 从零系列--node爬虫利用进程池写数据

    1.主进程 const http = require('http'); const fs = require('fs'); const cheerio = require('cheerio'); co ...

  3. Spring入门学习笔记(2)——基于Java的配置

    目录 基于Java的配置 @Configuration & @Bean Annotations Example 注入Bean依赖 @Import注解 Lifecycle Callbacks(声 ...

  4. 带你轻而易举的学习python——八皇后问题

    首先我们来看一下这个著名的八皇后问题 八皇后问题:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 在这个问题提出之后人们又将 ...

  5. 对React children 的深入理解

    React的核心为组件.你可以像嵌套HTML标签一样嵌套使用这些组件,这使得编写JSX更加容易因为它类似于标记语言. 当我刚开始学习React时,当时我认为“使用 props.children 就这么 ...

  6. JavaWeb项目通过调用cmd实现备份数据库的功能

    1.别急着上车,先测试一下能否成功调用cmd,可以尝试通过cmd命令打开计算器,代码如下:     2.能成功打开计算器后,证明调用cmd的方法是没错的,现在把cmd命令字符串改成我们备份数据库的 命 ...

  7. 5233杨光--Linux第二次实验

    实验说明 1. 环境登录 无需密码自动登录,系统用户名shiyanlou,密码shiyanlou 若不小心登出后,直接刷新页面即可 2. 环境使用 完成实验后可以点击桌面上方的“实验截图”保存并分享实 ...

  8. 软件项目的开发之svn的使用

    Svn简介 SVN全名Subversion,即版本控制系统.SVN与CVS一样,是一个跨平台的软件,支持大多数常见的操作系统.作为一个开源的版本控制系统,Subversion管理着随时间改变的数据.这 ...

  9. Java script 中的面向对象1

    Java script 中的面向对象 对象 对象是Javascript的基本数据类型,对象是一种复合值,将很多的键值对聚合在一起使用.对象可看做是属性的无序集合,每个属性都是一个名/值对.属性名其实是 ...

  10. Beta Scrum Day 4 — 听说

    听说