zoukankan      html  css  js  c++  java
  • 【洛谷6302】[NOI2019] 回家路线 加强版(斜率优化)

    点此看题面

    • 有一张(n)个点的图和三个参数(A,B,C),你需要从(1)号点走到(n)号点。
    • (m)条有向边,每条边有一个出发时间和到达时间,只有在出发时间之前到达出发点才能选择走这条边。
    • 走两条边的间隔可能存在等待时间,假设一次等待时间为(x),则需要花费(Ax^2+Bx+C)的代价。
    • 假设你在(t)时刻到达(n)号点,还需要额外花费(t)的代价,求最小的总代价。
    • (nle10^5,mle10^6)

    理清思路

    题目看起来搞得人很晕,一张图上(DP)看起来走来走去很容易形成环,没有固定的转移顺序。

    但是,仔细理一理思路,便会发现,一次转移之后时间是递增的,所以我们应该按照时间顺序来转移。

    因此设(f_{i,ti})表示恰好在(ti)时刻到达(i)号点所需的最小花费。(注意恰好,因为这道题对等待时间有要求)

    然后只要先枚举时间,再枚举这个时间的每一条边转移即可。

    斜率优化(DP)

    考虑一条边((x,y,p,q)),就是要让(f_{y,q})(f_{x,i}(ile p))转移。

    其中(ile p)的限制是很好处理的,我们原本就是在枚举时间,只要在时刻(p)把所有可能的(f_{x,p})加入(x)的可选转移状态中即可。(注意,由于要求了恰好,因此可能的状态数是(O(m))的)

    列出一次转移的方程式:

    [f_{y,q}=max{A(p-i)^2+B(p-i)+C+f_{x,i}} ]

    这一看就超级斜率优化,于是我们先把转移式拆开:

    [A(p-i)^2+B(p-i)+C+f_{x,i}\ Ap^2-2Api+Ai^2+Bp-Bi+C+f_{x,i}\ (Ap^2+Bp+C)+(Ai^2-Bi+f_{x,i})-2Api ]

    写出(f_{x,i})优于(f_{x,j})(i<j))的充要条件:

    [(Ai^2-Bi+f_{x,i})-2Api<(Aj^2-Bj+f_{x,j})-2Apj\ 2Ap(j-i)<(Aj^2-Bj+f_{x,j})-(Ai^2-Bi+f_{x,i})\ 2Ap<frac{(Aj^2-Bj+f_{x,j})-(Ai^2-Bi+f_{x,i})}{j-i} ]

    然后发现由于我们是按照时间转移的,(p)单调递增,因此只要对于每个点维护好一个斜率递增的单调队列即可。

    代码:(O(m))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define M 1000000
    #define V 40000
    #define LL long long
    #define INF 1e18
    using namespace std;
    int n,m,A,B,C;LL ans=1e18;
    struct E {int x,y,t;I E(CI a=0,CI b=0,CI k=0):x(a),y(b),t(k){}};vector<E> e[V+5];vector<E>::iterator et;
    struct O {int x;LL v;I O(CI i=0,Con LL& s=0):x(i),v(s){}};vector<O> o[V+5];vector<O>::iterator ot;
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    }using namespace FastIO;
    namespace Slope//斜率优化
    {
    	#define b(p) (1LL*A*p.ti*p.ti-1LL*B*p.ti+p.v)//计算截距
    	struct P {int ti;LL v;I P(CI t=0,Con LL& s=0):ti(t),v(s){}};
    	deque<P> Q[N+5];deque<P>::iterator t1,t2;
    	I double S(Con P& x,Con P& y) {return x.ti^y.ti?1.0*(b(y)-b(x))/(y.ti-x.ti):(y.v>x.v?1e18:-1e18);}//计算斜率
    	I void Push(CI x,Con P& p)//往x的单调队列里面加入p
    	{
    		if(x==n) return (void)(ans=min(ans,p.ti+p.v));if(Q[x].empty()) return Q[x].push_back(p);//x=n时统计答案
    		t1=t2=--Q[x].end();W(t1!=Q[x].begin()) if(--t1,S(*t1,*t2)>=S(*t2,p)) --t2,Q[x].pop_back();else break;//弹出队尾不优元素
    		Q[x].push_back(p);//加入队列
    	}
    	I void DP(CI x,CI y,CI p,CI q)//边(x,y,p,q)的一次转移
    	{
    		t1=t2=Q[x].begin(),++t2;
    		W(t2!=Q[x].end()) if(S(*t1,*t2)<2*A*p) ++t1,++t2,Q[x].pop_front();else break;//弹出队首不优元素
    		LL t=1LL*A*(p-t1->ti)*(p-t1->ti)+1LL*B*(p-t1->ti)+C+t1->v;o[q].push_back(O(y,t));//从队首转移
    	}
    }
    int main()
    {
    	RI i,x,y,p,q;for(read(n,m,A,B,C),i=1;i<=m;++i) read(x,y,p,q),e[p].push_back(E(x,y,q));//把边扔到对应时间的桶里
    	using namespace Slope;for(o[0].push_back(O(1,0)),i=0;i<=V;++i)//最初只有1号点有合法状态
    	{
    		for(ot=o[i].begin();ot!=o[i].end();++ot) Push(ot->x,P(i,ot->v));//把合法状态加入对应的单调队列
    		for(et=e[i].begin();et!=e[i].end();++et) DP(et->x,et->y,i,et->t);//枚举边转移
    	}return printf("%lld
    ",ans),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    python基本数据类型之字符串(二)
    python基本数据类型之字符串(一)
    Java基础之Java简介
    1024lab-How to run project using .ipynb
    pytorch调试工具
    关于深度学习选择和使用GPU
    中文文本预处理
    GCN相关
    Bert project Debug记录
    图卷积相关的参考
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6302.html
Copyright © 2011-2022 走看看