codeforces 553E - Kyoya and Train

一个有向图,走每条边有个代价,且花费的时间从1~T有个概率p[e][i],从1到n,若到达时时间超过T,则需要额外X的花费,问期望最小花费

令f[i][j]表示在j时刻从i出发到达n的最小期望,因为虽然每条边花费的时间是不确定的,但至少是1,所以(i,j)这个分层图实际上是一个DAG,就可以dp了

对于一条边 e:u>v ,有 f[u][t]=min(ei+Tj=1f[v][t+j]p[e][j])
当t>T时,直接走i->n的最短路就行了,于是 f[u][t]=s[u][n]+X ,预处理i->n的最短路后可以O(1)算
又因为边的耗时在1~T内,所以第二维最多只需要算到2T
但直接dp的话,状态数是 nT 的,每次转移是 T 的,总复杂度 nT2
考虑优化,右边那个东西只和边和时间有关,我们令
g[e][t]=ei+Tj=1f[v][t+j]p[e][j]
然后把坐标变换一下,变换成卷积的形式,接着对时间进行分治(cdq),f和g互相更新,l=r时f[i][l]对出边的g[e][l]取min就行了

code:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define inf 1e9
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
inline void down(int &x,const int &y){if(x>y)x=y;}
const double pi = acos(-1);
const int maxn = 110;
const int maxm = 110;
const int maxt = 41000; 

struct E
{
    double x,y;
    E(){}
    E(const double _x,const double _y){x=_x;y=_y;}
}w[maxt<<1],s1[maxt<<1],s2[maxt<<1]; int id[maxt<<1],N,ln;
inline E operator +(const E &x,const E &y){return E(x.x+y.x,x.y+y.y);}
inline E operator -(const E &x,const E &y){return E(x.x-y.x,x.y-y.y);}
inline E operator *(const E &x,const E &y){return E(x.x*y.x-x.y*y.y,x.x*y.y+x.y*y.x);}

void DFT(E s[],const int sig)
{
    for(int i=0;iif(ifor(int m=2;m<=N;m<<=1)
    {
        int t=m>>1,tt=N/m;
        for(int j=0;jfor(int i=0;i1?w[i*tt]:w[N-i*tt];
                E tx=s[j+i],ty=s[j+i+t]*wn;
                s[j+i]=tx+ty;
                s[j+i+t]=tx-ty;
            }
        }
    }
    if(sig==-1) for(int i=0;idouble)N;
}
int n,m,T,X;
double e[maxm][maxt],f[maxn][maxt];
double p[maxm][maxt];
int u[maxm],v[maxm],ci[maxm];

int s[maxn][maxn];

void calc(const int l,const int mid,const int r)
{
    int len=r-l+1;
    N=1,ln=0; while(N<=len+r-mid) N<<=1,ln++;
    for(int i=1;i>1]>>1)|((i&1)<1);
    for(int M=2;M<=N;M<<=1)
    {
        int t=M>>1,tt=N/M;
        for(int i=0;icos(2*pi*i/M),sin(2*pi*i/M)),
            w[N-i*tt]=E(cos(2*pi*i/M),sin(-2*pi*i/M));
    }

    for(int i=1;i<=m;i++)
    {
        for(int j=0;j0,0);
        for(int j=mid;j<=r;j++) s1[r-j]=E(f[v[i]][j],0);
        for(int j=1;j<=len;j++) s2[j]=E(p[i][j],0);
        DFT(s1,1); DFT(s2,1);
        for(int j=0;j1);
        for(int j=l;jvoid solve(int l,int r)
{
    if(l==r) 
    {
        for(int i=1;ifor(int i=1;i<=m;i++)
        {
            double temp=e[i][l]+ci[i];
            if(f[u[i]][l]>temp) f[u[i]][l]=temp;
        }
        return;
    }
    int mid=l+r+1>>1;
    solve(mid,r);
    calc(l,mid,r);
    solve(l,mid-1);
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&T,&X);
    for(int i=1;i<=m;i++) 
    {
        read(u[i]),read(v[i]),read(ci[i]);
        for(int j=1;j<=T;j++)
        {
            int x; read(x);
            p[i][j]=(double)x/100000;
        }
    }

    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) s[i][j]=inf;
    for(int i=1;i<=m;i++) s[u[i]][v[i]]=ci[i];
    for(int i=1;i<=n;i++) s[i][i]=0;
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++) if(i!=k)
            for(int j=1;j<=n;j++) if(i!=j&&k!=j)
                down(s[i][j],s[i][k]+s[k][j]);
    for(int i=1;i<=n;i++) for(int j=T+1;j<=T*2;j++) f[i][j]=X+s[i][n];
    for(int i=1;i<=T;i++) f[n][i]=0;
    calc(0,T+1,2*T);

    solve(0,T);
    printf("%.10lf\n",f[1][0]);

    return 0;
}

你可能感兴趣的:(codeforces,快速傅里叶变换(FFT),DP,分治,最短路)