标签:ati code ack 复杂度 处理 printf long def 分治
还是那句话,我觉得分治FFT是有实现难度的 初学,dalao别D
看完题第一反应应该是设 \(dp(i,j)\) 表示时刻 \(j\) 到达点 \(i\) 的最小代价。
发现完全不会处理边界,想了一会就舍掉了。
设 \(dp(i,j)\) 表示时刻 \(j\) 在 \(i\) 到达 \(n\) 的最小代价,那么答案就是 \(dp(1,0)\)
这个是可以转移的。
暴力是 \(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\) 把 \(l\) 更新掉就好了
转移大概长这样:
(钦定 \(P_{i,0}=0\)看着舒服)
用cdq分治的思路,不断统计右区间对左区间的贡献就能算出 \(g\) 了。
然后就是非常烦的边界处理,一定想清楚再打,尽量不要调。但是我边界对了FFT打挂了是我没想到的
令 \(A_j=P_{i,j}(l\le j\le r),B_{r-j+mid+1}=f(b_i,r-j)(mid+1\le r-j\le 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\) 这一项加到 \(j\in [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\n",f[1][0]);
return 0;
}
标签:ati code ack 复杂度 处理 printf long def 分治
原文地址:https://www.cnblogs.com/zzctommy/p/14209728.html