P5468 & P6302 [NOI2019] 回家路线 - 斜率优化

题解

我们将所有边按照 p 从小到大排序。显然此时每条边都只会从它前面的边转移过来。

u(e),v(e),p(e),q(e) 分别为边 e 的四个参数。设 fe 为通过边 e 到达 v(e),花费的最小烦躁值(先不考虑烦躁值中单独的一项 q)。所以:

fe=minv(e)=u(e)q(e)p(e){fe+A(p(e)q(e))2+B(p(e)q(e))+C}

答案即为 minv(e)=nfe+qe

我们将 f 的转移方程整理成斜率优化的形式:

fe=minv(e)=u(e)q(e)p(e){fe+Aq2(e)Bq(e)2Aq(e)p(e)}+Ap2(e)+Bp(e)+C

我们维护 n 个凸壳,第 i 个凸壳内的任意点 x 都满足 v(x)=i。因为 p(e) 单调递增,所以可以线性地维护这个凸壳。当我们计算完一个 fe,我们先将它放在下标为 q(e) 的桶里,当我们要计算一个 fe 满足 p(e)q(e) 时,再把这些桶里的点都放到凸壳里。

复杂度 O(n+m)

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <deque>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
#define Debug(...) fprintf(stderr,__VA_ARGS__)
typedef long long ll;
typedef __int128 i128;
const int N=2e5+5,M=2e6+5;
const ll Inf=0x3f3f3f3f3f3f3f3f;
int n,m;ll A,B,C;
struct Edge{
	int u,v;ll p,q;
}edge[M];
ll f[M];
auto X=[](int i){return edge[i].q;};
auto Y=[](int i){return f[i]+A*edge[i].q*edge[i].q-B*edge[i].q;};
bool Pop(int x,int y,int z){
	return i128(Y(y)-Y(x))*(X(z)-X(y))>=i128(Y(z)-Y(y))*(X(y)-X(x));
}
ll W(int x,ll k){return f[x]+A*edge[x].q*edge[x].q-B*edge[x].q-2*A*k*edge[x].q;}
deque<int> dq[N];
vector<int> buk[N];
int main(){
	#ifndef zyz
	ios::sync_with_stdio(false),cin.tie(nullptr);
	#endif
	cin>>n>>m>>A>>B>>C;
	For(i,1,m){
		cin>>edge[i].u>>edge[i].v>>edge[i].p>>edge[i].q;
	}
	sort(edge+1,edge+m+1,[](const Edge &e1,const Edge &e2){return e1.p<e2.p;});
	int cur=0;
	memset(f,0x3f,sizeof f);
	For(i,1,m){
		int u=edge[i].u;ll p=edge[i].p,q=edge[i].q;
		if(u==1){
			f[i]=A*p*p+B*p+C;
		}
		for(;cur<=p;++cur){
			for(int e:buk[cur]){
				int x=edge[e].v;
				while(dq[x].size()>1&&Pop(dq[x][dq[x].size()-2],dq[x].back(),e)) dq[x].pop_back();
				dq[x].push_back(e);
			}
			buk[cur].clear();
		}
		while(dq[u].size()>1&&W(dq[u].front(),p)>=W(dq[u][1],p)) dq[u].pop_front();
		if(dq[u].size()) f[i]=min(f[i],W(dq[u].front(),p)+A*p*p+B*p+C);
		buk[q].push_back(i);
	}
	ll ans=Inf;
	For(i,1,m) if(edge[i].v==n) ans=min(ans,edge[i].q+f[i]);
	cout<<ans;
	return 0;
}

坑点

若一条边 e 满足 p(e)=1,那直接让 fe=Ap2(e)+Bp(e)+C 不一定是最优的,可能绕一个圈回来。

作者:alan-zhao-2007

出处:https://www.cnblogs.com/alan-zhao-2007/p/p5468-sol.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Alan_Zhao_2007  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题