NOI 2019 回家路线 题解

题目传送门

题目大意: 一只猫要从 1 1 1 号站点坐车到 n n n 号站点,第 i i i 班车从 x i x_i xi 开到 y i y_i yi,在 p i p_i pi 发车, q i q_i qi 到达,假如在 a a a 时刻下车等到 b b b 时刻再上车,那么烦躁值会增加 A ( b − a ) 2 + B ( b − a ) + C A(b-a)^2+B(b-a)+C A(ba)2+B(ba)+C。如果在 c c c 时刻到达了 n n n 号站点,那么烦躁值会再增加 c c c,求最小烦躁值。

题解

f ( i ) f(i) f(i) 表示搭第 i i i 班车后的最小烦躁值,这里先不考虑到达 n n n 时增加的 c c c 烦躁值。那么有:
f ( i ) = min ⁡ { f ( j ) + A ( p i − q j ) 2 + B ( p i − q j ) + C } f(i)=\min\{f(j)+A(p_i-q_j)^2+B(p_i-q_j)+C\} f(i)=min{ f(j)+A(piqj)2+B(piqj)+C}

其中, j j j 需要满足 y j = x i y_j=x_i yj=xi q j ≤ p i q_j\leq p_i qjpi

先不考虑这两个限制,推推柿子,发现它是斜率优化的形式,令 g ( j ) = f ( j ) + A q j 2 − B q j g(j)=f(j)+Aq_j^2-Bq_j g(j)=f(j)+Aqj2Bqj,则 j j j 优于 k k k 需要满足 g ( j ) − g ( k ) q j − q k ≤ 2 A p i \dfrac {g(j)-g(k)} {q_j-q_k}\leq 2Ap_i qjqkg(j)g(k)2Api

然后考虑上面的限制, y j = x i y_j=x_i yj=xi 这个很好解决,我们在每个位置开个队列,转移点 ( q j , g ( j ) ) (q_j,g(j)) (qj,g(j)) 放到第 y j y_j yj 个栈中,而 i i i 转移时,从第 x i x_i xi 个队列中找转移点即可。

另一个关于时间的限制也很好处理,将所有时间点排序即可,令 i i i 列车在 p i p_i pi 时转移, q i q_i qi 时才放入队列即可。

然后就是注意一点细节问题了,代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define pb push_back

int n,m,A,B,C,ans=2e9;
int p[200010],q[200010],X[200010],Y[200010];
vector<int> c1[1010],c2[1010];
int f[200010],g[200010];
struct Queue{
    
    
	vector<int> s;
	int st,ed;
	Queue():st(0),ed(-1){
    
    }
	bool empty(){
    
    return st>ed;}
	int front(){
    
    return s[st];}
	void add(int x){
    
    
		while(st<ed&& 1ll*(g[s[ed]]-g[s[ed-1]])*(q[x]-q[s[ed]]) >= 1ll*(g[x]-g[s[ed]])*(q[s[ed]]-q[s[ed-1]]))ed--;
		if(s.size()-1==ed)s.pb(0);
		s[++ed]=x;
	}
	void del(int x){
    
    
		while(st<ed&& (g[s[st+1]]-g[s[st]]) < 1ll*x*(q[s[st+1]]-q[s[st]]) )st++;
	}
}Q[100010];

int main()
{
    
    
	scanf("%d %d %d %d %d",&n,&m,&A,&B,&C);
	for(int i=1;i<=m;i++){
    
    
		scanf("%d %d %d %d",&X[i],&Y[i],&p[i],&q[i]);
		c1[p[i]].pb(i);
		c2[q[i]].pb(i);
	}
	memset(f,63,sizeof(f));
	memset(g,63,sizeof(g));//g也要初始化,别忘了
	f[0]=0;g[0]=0;Q[1].add(0);
	for(int Ti=0;Ti<=1000;Ti++){
    
    
		for(int i:c2[Ti]){
    
    //注意要先处理c2,即先将点加入队列,下面的c1可能用到
			if(Y[i]==n)ans=min(ans,f[i]+q[i]);
			else Q[Y[i]].add(i);
		}
		for(int i:c1[Ti]){
    
    
			if(!Q[X[i]].empty()){
    
    
				Q[X[i]].del(2*A*p[i]);
				int j=Q[X[i]].front();
				f[i]=f[j]+A*(p[i]-q[j])*(p[i]-q[j])+B*(p[i]-q[j])+C;
				g[i]=f[i]+A*q[i]*q[i]-B*q[i];
			}
		}
	}
	printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/112957341