zoukankan      html  css  js  c++  java
  • P5468 [NOI2019] 回家路线

    P5468 [NOI2019] 回家路线

    斜率优化入门(指我写的东西而不是这道题)

    题意

    (n) 个节点,有 (m) 趟列车,车 (i)(p_i) 时刻至 (q_i) 时刻从 (x_i) 地到 (y_i) 地。猫猫需要只坐列车从 (1) 节点到达 (n) 节点。

    设猫猫在一个地方等待的时间为 (t),那么代价为 (At^2+Bt+C),其中 (A B C) 为给定非负常数,总代价为所有代价总和与到达时间的和。求最小代价。

    思路

    (f_i) 为走 (i) 趟列车前的最小代价,有转移方程:

    [f_i=min_{y_j=x_iland q_jleq q_i}(f_j+A(p_i-q_j)^2+B(p_i-q_j)+C) ]

    最终代价即为:

    [min f_i+q_i ]

    对于第一个方程,不考虑其他限制,即为找到最优的 (j) 更新 (i),有:

    [egin{aligned} f_i&=f_j+A(p_i-q_j)^2+B(p_i-q_j)+C\ 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 end{aligned} ]

    对于已经更新过的 (j)(f_j+Aq_j^2-Bq_j) 是已知的。后半部分 (f_i-Ap_i^2-Bp_i-C) 只有 (f_i) 是未知的,也是我们要最优化的。

    那么我们可以将上式看做 (y=kx+b) 的形式,其中

    [egin{aligned} &y=f_j+Aq_j^2-Bq_j\ &x=q_j\ &k=2Ap_i\ &b=f_i-Ap_i^2-Bp_i-C end{aligned} ]

    (常数其实可以属于任意一个位置,上述只是举例)

    那么我们可以将所有可以转移到的 (j) 看做一个二维平面上的点,每次更新 (f_i) 实际是对一个斜率找出经过其中一个点得到的最优的 (b)。那么显然只会选到前者构成的凸包上的点。很多题查询的斜率都是单调的,而且之后在凸包后方插入新的点,于是维护凸包的数组就变成了单调队列。否则的话,就是动态维护凸包并在凸包上二分。

    对于这道题,最优指最小,那么我们维护的是下凸包。

    斜率优化的部分已经完了。考虑加入 (y_j=x_i and q_jleq p_i) 的限制怎么做。对于第一个限制,发现不同点互不影响,那么我们对每个点开一个单调队列,对于一个列车在它的 (x) 位置查询就好了。对于第二个限制,我们将所有列车按照 (p) 排序然后枚举时间,那么对于上面更新完的列车先不插入队列,当枚举时间到 (p) 的时候再将其插入即可。这样就能满足所有限制,并且保证查询的斜率单调递增。

    代码

    好像不会炸 long long 的样子。

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
    	int w=0,x=0;char c=getchar();
    	while(!isdigit(c))w|=c=='-',c=getchar();
    	while(isdigit(c))x=x*10+(c^48),c=getchar();
    	return w?-x:x;
    }
    namespace star
    {
    	const int maxn=1e6+10;
    	int n,T,m,A,B,C,st[maxn],ed[maxn],p[maxn],q[maxn],hd[maxn],tl[maxn];
    	long long f[maxn],x[maxn],y[maxn],ans=0x3f3f3f3f3f3f3f3f;
    	vector<int> V[maxn],G[maxn],que[maxn];
    	inline void work(){
    		n=read(),m=read(),A=read(),B=read(),C=read();
    		for(int i=1;i<=m;i++) st[i]=read(),ed[i]=read(),p[i]=read(),q[i]=read(),T=max(T,q[i]),G[p[i]].push_back(i);
    		for(int i=1;i<=n;i++) hd[i]=0,tl[i]=-1;
    		for(int t=0;t<=T;t++){
    			for(auto i:V[t]){
    				int e=ed[i];
    				while(hd[e]<tl[e] and 1ll*(y[que[e][tl[e]]]-y[que[e][tl[e]-1]])*(x[i]-x[que[e][tl[e]]])>=1ll*(y[i]-y[que[e][tl[e]]])*(x[que[e][tl[e]]]-x[que[e][tl[e]-1]])) --tl[e];
    				if(++tl[e]==que[e].size()) que[e].push_back(i);
    					else que[e][tl[e]]=i;
    			}
    			for(auto i:G[t]){
    				int e=st[i];
    				while(hd[e]<tl[e] and (y[que[e][hd[e]+1]]-y[que[e][hd[e]]])<2ll*A*p[i]*(x[que[e][hd[e]+1]]-x[que[e][hd[e]]])) ++hd[e];
    				if(hd[e]>tl[e] and st[i]!=1) continue;
    				int j=st[i]==1 and hd[e]>tl[e]?0:que[e][hd[e]];
    				f[i]=f[j]+1ll*A*(p[i]-q[j])*(p[i]-q[j])+1ll*B*(p[i]-q[j])+C;
    				x[i]=q[i],y[i]=f[i]+1ll*A*q[i]*q[i]-1ll*B*q[i];
    				V[q[i]].push_back(i);
    				if(ed[i]==n) ans=min(ans,f[i]+q[i]);
    			}
    		}
    		printf("%lld
    ",ans);
    	}
    }
    signed main(){
    	star::work();
    	return 0;
    }
    
  • 相关阅读:
    php用redis保存session
    php字符串常用算法--字符串加密解密
    PHP file_get_contents设置超时处理方法
    php栈数据结构和括号匹配算法
    JavaScript 函数参数传递到底是值传递还是引用传递
    JavaScript数据操作--原始值和引用值的操作本质
    JavaScript数据类型--值类型和引用类型
    js基本数据类型和typeof
    JS获取字符串实际长度(包含汉字)
    sublime的一些插件
  • 原文地址:https://www.cnblogs.com/BrotherHood/p/14846645.html
Copyright © 2011-2022 走看看