P5468 P6302 [NOI2019]回家路线

原题P5468洛谷加强版P6302LOJ3156
听说原题是用脚造的数据,所以可能无法把代码的锅都测出来,所以还是去加强版吧
做了好长时间,主要是原版得分玄学,有的时候把代码一个地方改错甚至得了更多的分(当时同时还存在其它的错)?/jk,然后加强版又被卡常/kk

斜率优化dp+vector 维护凸包+堆维护还没放入凸包的点

首先想怎么设计状态,一开始想的是 (f_{i,j}) 表示时间 (i),刚达到车站 (j),的最小烦躁值
但这样显然会产生很多无用状态,就是可能在某一个时间 (i),没有到车站 (j) 的车,那这一个状态就是无用的
所以改进一下,设 (f_i) 为坐完编号为 (i) 的车时,的最小烦躁值,这样可以由车的编号确定到达时间了
然后对于所有 (y_i=n) 的车,(min{f_i+q_i}) 即为答案

然后考虑怎么转移,(f_i)(f_j) 转移而来的条件是,(q_jle p_i,y_j=x_i),所以可以吧防尘写成这样:

[f_i=min_{q_jle p_i,y_j=x_i}{f_j+A(p_i-q_j)^2+B(p_i-q_j)+C} ]

那直接暴力转移就是 (O(n^2)) 了,能得 (70),再加上有 (20) 的特殊限制的部分分,那谁还为了最后十分写dp优化啊

还是要写的,看着有一个平方项,展开的话就有一项是和 (i,j) 都有关,那就能想到斜率优化了
其实感觉好多斜率优化的题都是用这么一个平方来创造那个 (kx),就是和 (i,j) 都有关的项(比如经典的玩具装箱)
那就先不考虑限制条件,化简式子:

[f_i=f_j+Ap_i^2+Aq_j^2-2Ap_iq_j+Bp_i-Bq_j+C ]

[f_j+Aq_j^2-Bq_j=2Ap_iq_j+(f_i-Ap_i^2-Bp_i-C) ]

也就是根据斜率优化常见的套路,把等号左边和 (j) 有关的设为 (Y),括号里和 (i) 有关的(包括常数)设为 (b),且 (K=2Ap_i,X=q_j)
所以维护一个下凸包,最小化斜率即可

这时的把限制条件考虑回来
(y_j=x_i) 这个,只要对每个点都维护一个凸包,(f_i) 从第 (x_i) 个凸包转移,转移结束,就加入到第 (y_i) 个凸包里

这个 (q_jle p_i) 稍微有点麻烦,因为 (p_j<q_j),所以 (p_j<p_i),那我们应该把所有车按 (p) 升序排序来处理
但这个 (p_j<p_i) 只是说,如果限制条件满足,一定满足他,但满足他不一定满足限制条件一开始没想清楚被这个坑了
所以此时再维护 (n) 个小根堆,用 (q) 排序,然后对于每个 (i),就把第 (x_i) 个堆中,所有 (q_jle p_i)(j) 都取出,并放入第 (x_i) 个 vector 维护成凸包
直到发现一个点 (q_j>p_i) 了,那后面的 (j) 也都不符合要求,插入结束
如果满足 (q_jle p_i)(j) 被插入凸包了,那由于 (p) 不减,后面的 (i) 肯定也满足了这个条件了
然后 (f_i) 转移完 (i) 就也不是直接插入凸包里,而是插入对应的堆中,等他符合了相应条件再插入凸包

这也不妨是一个更普适的思路,就是当用一个数据结构维护点东西(比如凸包)来优化dp时,一个状态被转移完,如果还不符合用它转移其它状态的要求,就先用另一个数据结构存起来

还会发现这样的话 (K,X) 都递增了,可以用之间说的 vector 维护

这样轻松过了原版和LOJ,但发现在加强版T成了40
于是愉快开始卡常,一开始是用 set 当堆来维护,然后换成优先队列,稍微快了点,但还是40
然后又加了行编译优化,依然40,好像没啥用
也许是stl用的太多,不然一秒 (10^6)(O(mlog m)) 应该挺好过吧,但因为是动态内存也不好避免stl
于是现学现卖fread,然后边写周末作业边等到半夜,一交最大点997ms卡过果然卡常的最好方式是等人少的时候提交
submit+=20

欢迎大家来看看我的代码常数为啥这么大/kk

#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
namespace Fread{
	char buffer[32768],*cs,*ct;
	#define get_char (cs==ct&&(ct=(cs=buffer)+fread(buffer,1,32768,stdin),cs==ct)?0:*cs++)
	inline int read(){
		register int x=0;
		register char c=get_char;
		while(c<'0'||c>'9') c=get_char;
		while(c>='0'&&c<='9') x=x*10+(c^48),c=get_char;
		return x;
	}
	#undef getc
}
using namespace Fread;
struct data{
	int x,y;
	LL p,q;
//	int id;
}t[1000006];
int n,m;
LL A,B,C;
LL f[1000005];
LL X[1000001],Y[1000001];
std::vector<int>q[1000001];
struct cmp{
	inline int operator()(const int &aa,const int &bb){
		return t[aa].q==t[bb].q?aa>bb:t[aa].q<t[bb].q;
	}
};
struct _insert{
	int i;
	inline int operator <(const _insert &a)const{return t[i].q>t[a.i].q;}//按 q 的升序
};
std::priority_queue<_insert>insert[1000001];
inline int cmp(data a,data b){return a.p<b.p;}
inline LL min(LL a,LL b){return a>b?b:a;}
//inline LL X(int i){return t[i].q;}
//inline LL Y(int i){return f[i]+A*t[i].q*t[i].q-B*t[i].q;}
inline void Insert(int i){
	reg int size,j,head;//size 为 set insert 的大小,head 为 vector q[x[i]] 最后一个数的下标
	size=insert[t[i].x].size();
	if(!size) return;
	j=insert[t[i].x].top().i;
	while(size&&t[j].q<=t[i].p){
		head=q[t[i].x].size()-1;
		while(head>0){
			if((Y[q[t[i].x][head]]-Y[q[t[i].x][head-1]])*(X[j]-X[q[t[i].x][head]])
			>=(Y[j]-Y[q[t[i].x][head]])*(X[q[t[i].x][head]]-X[q[t[i].x][head-1]])) q[t[i].x].pop_back();
			else break;
			head--;
		}
		q[t[i].x].push_back(j);
		size--;insert[t[i].x].pop();
		j=insert[t[i].x].top().i;
	}
}
inline void pop(int i){
	reg int size=q[t[i].x].size();
	reg LL K=2*A*t[i].p;
	while(size>1&&Y[q[t[i].x][1]]-Y[q[t[i].x][0]]<=K*(X[q[t[i].x][1]]-X[q[t[i].x][0]])){
		size--;
		q[t[i].x].erase(q[t[i].x].begin());
	}
}
int main(){
//		std::freopen("P6302_2.in","r",stdin);
//		std::freopen("tmp.txt","w",stdout);
	n=read();m=read();A=read();B=read();C=read();
	for(reg int i=1;i<=m;i++)
		t[i].x=read(),t[i].y=read(),t[i].p=read(),t[i].q=read(),f[i]=1e10;
	std::sort(t+1,t+1+m,cmp);
	f[0]=0;
	q[1].push_back(0);
	for(reg int i=1,j;i<=m;i++){//k=2*A*p[i]
		Insert(i);pop(i);
		if(q[t[i].x].size()){
			j=q[t[i].x][0];
			f[i]=f[j]+A*(t[i].p-t[j].q)*(t[i].p-t[j].q)+B*(t[i].p-t[j].q)+C;
			insert[t[i].y].push((_insert){i});
		}
		X[i]=t[i].q;Y[i]=f[i]+A*t[i].q*t[i].q-B*t[i].q;
//			if(f[i]<0){
//				std::printf("i: %d, f[i]: %lld, from: j: %d, f[j]: %lld
",i,f[i],j,f[j]);
//				std::printf("%d: start : %lld, end : %lld
%d: start : %lld, end : %lld
",i,t[i].p,t[i].q,j,t[j].p,t[j].q);
//				if(t[j].q>t[i].p) std::puts("!!! q[j]>p[i] !!!");
//				return 0;
//			}
	}
	LL ans=1e18;
	for(reg int i=1;i<=m;i++)if(t[i].y==n) ans=min(ans,f[i]+t[i].q);
//		for(reg int i=1;i<=m;i++) std::printf("%lld
",f[i]);
	std::printf("%lld",ans);
	return 0;
}