P5468 [NOI2019]回家路线
看题目一眼斜率优化,然后写半天调不出来
结果错误的 dfs 有 95 分?暴力 SPFA 就 AC 了?
讲讲正解:
显然是斜率优化的式子:
先不考虑 q_{s_k} 的贡献,设 f[i] 表示当前时间为 i 的最小代价
如果不考虑位置关系,有 f[p_i]=f[q_j]+A(p_i-q_j)^2+B(p_i-q_j)+C
拆开来:f[p_i]=f[q_j]+Ap_i^2-2Ap_iq_j+Aq_j^2+Bp_i-Bq_j+C
换一下位置 2Ap_iq_j+f[p_i]-Ap_i^2-Bp_i-C=f[q_j]+Aq_j^2-Bq_j
那么 k=2Ap_i,x=q_j,b=f[p_i]-Ap_i^2-Bp_i-C,y=f[q_j]+Aq_j^2-Bq_j
显然直接斜率优化
但是有位置和时间关系,不妨把每列车按时间拆成两点 p,q 分别对应询问和插入,然后按时间拍好序
这样每列车出发时,时间在它之前到达的车都处理完了,可以直接计算贡献,在每列车到达时,再把新的贡献插入凸包
因为还有位置的限制关系,所以对每个位置开一个 vector 维护对应位置的凸包
然后就是具体的实现细节了...
发现维护凸包时是从右边插入,左边弹出,插入可以直接用 vector 的 push\_back,pop\_back
左边弹出可以对每个 vector 维护一个变量 pos 表示当前最左的合法点
对最终答案的更新可以在更新 dp 数组时特判
然后就要注意各种细节问题了(调到吐血)
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<vector> #include<queue> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(x=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=4e5+7,INF=2e9+7; int n,m,A,B,C,ans=INF,pos[N],val[N];//val维护每列火车的贡献 inline int calc(int t) { return A*t*t+B*t+C; } struct Train{//列车 int x,y,p,q; }T[N]; struct dat{//按时间排序的点 int tim,type,id;//type判断插入还是查询,id是火车编号 inline bool operator < (const dat &tmp) const { return tim<tmp.tim; } }D[N]; struct datt{//凸包的点 int tim,res;//时间,已经产生的代价 inline int X() { return tim; } inline int Y() { return res-A*tim*tim-B*tim; } }; vector <datt> V[N];//vector维护凸包 inline ll Cross(ll xa,ll xb,ll ya,ll yb) { return xa*yb-xb*ya; }//叉积判断直线之间的关系 inline int Y(int p) { return val[p]-A*T[p].q*T[p].q-B*T[p].q; } inline void push(int p)//插入 { int t=T[p].y; if(val[p]>=INF) return;//注意特判 while(int(V[t].size())-pos[t]>=2)//记得-pos[t] { int len=V[t].size(); if( Cross( T[p].q-V[t][len-2].X() , Y(p)-V[t][len-2].Y() , V[t][len-1].X()-V[t][len-2].X() , V[t][len-1].Y()-V[t][len-2].Y() ) < 0 ) break; V[t].pop_back();//vector自带函数 } V[t].push_back((datt){T[p].q,val[p]});//加入 } int main() { //freopen("route.in","r",stdin); //freopen("route.out","w",stdout); n=read(),m=read(),A=read(),B=read(),C=read(); int a,b,c,d,tot=0; for(int i=1;i<=m;i++) { a=read(),b=read(),c=read(),d=read(); T[i]=(Train){a,b,c,d}; D[++tot]=(dat){c,0,i}; D[++tot]=(dat){d,1,i};//把列车拆成两个点 } sort(D+1,D+tot+1); V[1].push_back((datt){0,0});//记得插入初始的点 memset(val,0x7f,sizeof(val)); val[0]=0;//注意val是0x7f,加一点就会爆int for(int i=1;i<=tot;i++) { int p=D[i].id,t=T[p].x; if(D[i].type) { push(p); continue; } while(int(V[t].size())-pos[t]>1 && V[t][pos[t]].res+calc(D[i].tim-V[t][pos[t]].tim) >= V[t][pos[t]+1].res+calc(D[i].tim-V[t][pos[t]+1].tim) ) pos[t]++; if(pos[t]<int(V[t].size())) val[p]=V[t][pos[t]].res+calc(D[i].tim-V[t][pos[t]].tim);//更新val if(T[p].y==n&&val[p]<INF) ans=min(ans,val[p]+T[p].q);//注意有些火车坐不上,而val如果是0x7f,加上一个值就会爆掉 } printf("%d\n",ans); return 0; }
顺便附上最暴力无脑的 SPFA:
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<queue> #include<vector> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(x=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1e5+7,M=1007,INF=2e9+7; struct Train{ int x,y,p,q; }T[N<<1]; struct dat{ int pos,tim; }; queue <dat> Q; vector <int> V[N]; int n,m,A,B,C,ans=INF; inline int clac(int t) { return A*t*t+B*t+C; } int dis[N][M]; bool vis[N][M]; int main() { freopen("route.in","r",stdin); freopen("route.out","w",stdout); n=read(),m=read(),A=read(),B=read(),C=read(); int a,b,c,d; for(int i=1;i<=m;i++) { a=read(),b=read(),c=read(),d=read(); T[i]=(Train){a,b,c,d}; V[a].push_back(i); } memset(dis,0x7f,sizeof(dis)); Q.push((dat){1,0}); vis[1][0]=1; dis[1][0]=0; while(!Q.empty()) { dat x=Q.front(); Q.pop(); vis[x.pos][x.tim]=0; int len=V[x.pos].size(); for(int i=0;i<len;i++) { int &p=V[x.pos][i]; if(x.tim>T[p].p) continue; int cst=clac(T[p].p-x.tim); if(dis[T[p].y][T[p].q]>dis[x.pos][x.tim]+cst) { dis[T[p].y][T[p].q]=dis[x.pos][x.tim]+cst; if(!vis[T[p].y][T[p].q]) Q.push((dat){T[p].y,T[p].q}),vis[T[p].y][T[p].q]=1; } } } for(int i=1;i<=m;i++) if(T[i].y==n) ans=min(ans,dis[n][T[i].q]+T[i].q); printf("%d\n",ans); return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(三):用.NET IoT库
· 【非技术】说说2024年我都干了些啥