zoukankan      html  css  js  c++  java
  • CodeForces 553E Kyoya and Train 动态规划 多项式 FFT 分治

    原文链接http://www.cnblogs.com/zhouzhendong/p/8847145.html

    题目传送门 - CodeForces 553E

    题意

      一个有$n$个节点$m$条边的有向图,每条边连接了$a_i$和$b_i$,花费为$c_i$。

      每次经过某一条边就要花费该边的$c_i$。

      第$i$条边耗时为$j$的概率为$p_{i,j}$。

      现在你从$1$开始走到$n$,如果你在$t$单位时间内(包括$t$)到了$n$,不需要任何额外花费,否则你要额外花费$x$。

      问你在最优策略下的期望花费最小为多少。

      (注意你每走一步都会根据当前情况制定最好的下一步)

      $$nleq 50 ,m leq 100, tleq 20000, xleq 10^6$$

    题解

      毛爷爷论文题。

      放上毛爷爷题解。

      

      我稍微加了点修改。

      于是我是不是不用写题解了??

      写一下我做这题的感受。

      首先自己想了好久yy出了一个倍增+$FFT$,复杂度和标算一样(当然好像是错的),然后觉得过不去。

      然后看标算看到分治,没往下看,继续自己yy,好像会了$2只log$,觉得很神奇,因为两只$log$过不去嘛,所以肯定有神奇的优化。

      想了很久还是不会,往下一看真的是两只$log$。QAQ。

      写代码也是难受,看着标算还是写了50分钟。

      关键是还写挂了。

      找了好久好久,猛然间发现我在$FFT$之前的给$A$、$B$数组赋值的时候,两次都赋给了$A$,难怪$FFT$结果一直是$0$,然后一边心态爆炸的吐槽,一边交了一发,还好$1A$了,不然心态更爆炸。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=55,M=105,T=20005,S=1<<15;
    double PI=acos(-1.0);
    int n,m,t,punish;
    int a[M],b[M],c[M],dis[N][N];
    double dp[N][T],sum[M][T],p[M][T];
    int s,d,Rev[S];
    struct C{
    	double r,i;
    	C(){}
    	C(double a,double b){r=a,i=b;}
    	C operator + (C x){return C(r+x.r,i+x.i);}
    	C operator - (C x){return C(r-x.r,i-x.i);}
    	C operator * (C x){return C(r*x.r-i*x.i,r*x.i+i*x.r);}
    }w[S],A[S],B[S];
    void FFT(C a[],int n){
    	for (int i=0;i<n;i++)
    		if (i<Rev[i])
    			swap(a[i],a[Rev[i]]);
    	for (int t=n>>1,d=1;d<n;d<<=1,t>>=1)
    		for (int i=0;i<n;i+=(d<<1))
    			for (int j=0;j<d;j++){
    				C tmp=w[t*j]*a[i+j+d];
    				a[i+j+d]=a[i+j]-tmp;
    				a[i+j]=a[i+j]+tmp;
    			}
    }
    void solve(int L,int R){
    	if (L==R){
    		for (int e=1;e<=m;e++)
    			dp[a[e]][L]=min(dp[a[e]][L],sum[e][L]+c[e]);
    		return;
    	}
    	int mid=(L+R)>>1;
    	solve(mid+1,R);
    	//sum[e][L...mid]+=dp[mid+1...R]#p[e][1...R-L]
    	//sum[e][L...mid]+=dp[mid+1...R]*p[e][R-L-1...0]
    	for (s=1,d=0;s<R-mid+R-L;s<<=1,d++);
    	for (int i=0;i<s;i++){
    		Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(d-1));
    		w[i]=C(cos(2*i*PI/s),sin(2*i*PI/s));
    	}
    	for (int e=1;e<=m;e++){
    		for (int i=0;i<s;i++)
    			A[i]=B[i]=C(0,0);
    		for (int i=mid+1;i<=R;i++)
    			A[i-mid-1]=C(dp[b[e]][i],0);
    		for (int i=1;i<=R-L;i++)
    			B[R-L-i]=C(p[e][i],0);
    		FFT(A,s),FFT(B,s);                                              
    		for (int i=0;i<s;i++)
    			A[i]=A[i]*B[i],w[i].i*=-1.0;
    		FFT(A,s);
    		for (int i=0;i<s;i++)
    			A[i].r/=s,w[i].i*=-1.0;
    		for (int i=L;i<=mid;i++)
    			sum[e][i]+=A[i-mid-1+(R-L)].r;
    	}
    	solve(L,mid);
    }
    int main(){
    	scanf("%d%d%d%d",&n,&m,&t,&punish);
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=n;j++)
    			dis[i][j]=i==j?0:1e9;
    	for (int i=1;i<=m;i++){
    		scanf("%d%d%d",&a[i],&b[i],&c[i]);
    		dis[a[i]][b[i]]=min(dis[a[i]][b[i]],c[i]);
    		for (int j=1;j<=t;j++)
    			scanf("%lf",&p[i][j]),p[i][j]/=100000;
    	}
    	for (int k=1;k<=n;k++)
    		for (int i=1;i<=n;i++)
    			for (int j=1;j<=n;j++)
    				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    	for (int i=0;i<N;i++)
    		for (int j=0;j<T;j++)
    			dp[i][j]=1e9;
    	for (int i=1;i<=n;i++)
    		dp[i][t+1]=punish+dis[i][n];
    	for (int i=0;i<=t;i++)
    		dp[n][i]=0;
    	memset(sum,0,sizeof sum);
    	for (int e=1;e<=m;e++){
    		double P=0;
    		for (int i=1;i<=t;i++){
    			P+=p[e][t-i+1];
    			sum[e][i]+=P*dp[b[e]][t+1];
    		}
    	}
    	solve(0,t);
    	printf("%.8lf",dp[1][0]);
    	return 0;
    }
    

      

  • 相关阅读:
    Lotus Notes中文档查询(转)
    MSSQL日志管理
    VS使用带临时表的存储过程
    TaskbarForm
    IT人士在离职后可以做的14件事情
    app.config数据库连接字符串的加密
    IT职场人,切不要一辈子靠技术生存
    wmi资料
    迁移成功
    【SpeC#】-C#的又一同胞兄弟
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF553E.html
Copyright © 2011-2022 走看看