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

    [NOI2019] 回家路线 题解

    第一次这么深入理解斜率优化的习题。

    题意

    现在有 (n) 个城市,城市之间有 (m) 条火车可以到达。

    第i条火车是从第 (x_i) 出发并到达 (y_i),是在 (p_i) 时间出发,并在 (q_i) 时间到达。

    火车只能够在前一辆到达后才能乘坐。

    设共乘坐了k辆火车,那么他的代价是(q_{s_{k}}+left(A imes p_{s_{1}}^{2}+B imes p_{s_{1}}+C ight)+sum_{j=1}^{k-1}left(Aleft(p_{s j+1}-q_{s_{j}} ight)^{2}+Bleft(p_{s_{j+1}}-q_{s_{j}} ight)+C ight)) .

    求最小代价。

    数据范围

    原版:(2≤n≤10^5,1≤m≤2×10^5,0le p_i<q_i le 10^3)

    加强版:(2≤n≤10^5,1le mle 10^6,0le p_i<q_ile 4 imes 10^4)

    题解

    对于原版这个极小的数据范围,有优雅的暴力dp方法:

    (dp(i,j)) 为在时间 (j) 到达 (i) 号点最优方案。

    把所有火车按 (p)(q) 排序后枚举火车进行转移即可。

    复杂度 (O(n imes q_i)) 。勉强可过。


    但是对于加强版呢?

    我们来考虑使用斜率优化dp。

    (dp_i) 表示乘坐第 (i) 辆火车后最小代价。

    那么有状态转移方程:

    [dp_i = minlimits_{y_j=x_i , q_jle p_i}{dp_j+A imes(p_i-q_j)^2 + B imes(p_i-q_j)+C } . ]

    尝试把这个式子化简一下:

    [dp_i = dp_j+A imes p_i^2-2A imes p_icdot q_j+A imes q_j^2 + B imes p_i-B imes q_j+C .qquad ({y_j=x_i , q_jle p_i}) ]

    移项可得:

    [dp_j+Acdot q_j^2-Bcdot q_j = (2Acdot p_icdot q_j)+(dp_i-Acdot p_i^2-Bcdot p_i-C). ]

    这是斜率优化的标准式子。其中

    [y=dp_j+Acdot q_j^2-Bcdot q_j,\ kx = (2Acdot p_icdot q_j),\ b = (dp_i-Acdot p_i^2-Bcdot p_i-C) ]

    但是这样就忽略了限制条件 (({y_j=x_i , q_jle p_i})).

    • 对于前一个限制:
      • 我们对于每一个节点 (i) 都维护一个下凸包。这样对于每一辆车 (i) ,它从 (x_i) 转移,并且计入 (y_i) 所在的凸包里。
      • 这个过程就不能自己开数组来解决了,必须借助vector。
    • 对于第二个限制:
      • 首先我们考虑:最开始一定要进行一步排序,让 (p_i) 单调递增。因为 (p_ige q_j\, , q_j > p_j Rightarrow p_i>p_j) 可以考虑所有情况。
      • 既然 (p_i) 已经是单调递增的了,这里计算需要让凸包中所有的 (q_jle p_j)(j) 都存在于凸包中。
      • 我们用一个优先队列维护已经转移过的 (j) ,按 (q) 排序 。对于一个准备计算的 (i) ,将所有满足第二个条件的 (j) 塞入所属凸包内。

    整理一下思路:

    • 对于一个 (i) ,先将优先队列内满足 (q_jle p_i) 的都取出放入凸包内。
    • 使用斜率优化转移求 (dp_i)
    • (i) 放入优先队列内等待转移给其他量。

    思考

    这道题提供了很好的思路。

    当用一个数据结构来优化dp时,一个状态被转移完,如果还不符合用它转移其它状态的要求,就先用另一个数据结构存起来。(本题使用优先队列)

    另外,现在才发现自己在优先队列优先级设定上还有知识漏洞。

    这里简单概述一下:优先队列是优先级大的在前。理解为与 sortcmp 相反。

    还有,斜率优化时应尽量把除化乘,避免精度丢失。(可能导致巨大的失分!)

    这道题是一道很好的题,大概调了两个小时才出来。它是值得的。

    代码

    #include <bits/stdc++.h>
    #define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    const int INF = 0x3f3f3f3f, N = 4e6+5, M = 4e6+5;
    typedef long long ll;
    typedef unsigned long long ull;
    inline ll read(){
      ll ret = 0 ; char ch = ' ' , c = getchar();
      while(!(c >= '0' && c <= '9')) ch = c , c = getchar();
      while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
      return ch == '-' ? -ret : ret;
    }
    #define int ll
    int n,m,A,B,C;
    struct Edge{int u,v,p,q,id;}e[M];
    inline bool sotcmp(const Edge a,const Edge b){return a.p < b.p;}
    struct pqcmp{inline bool operator () (const Edge a,const Edge b){return a.q > b.q;}};
    vector<int> vc[N];
    priority_queue<Edge,vector<Edge>,pqcmp> pq;
    int dp[M];
    inline int f(int x){return dp[x] + A*e[x].q*e[x].q - B*e[x].q;}
    inline int X(int x){return e[x].q;}
    inline void push(Edge a){
    	int v = a.v, id = a.id;
    	while(vc[v].size() >= 2){
    		int x = vc[v][vc[v].size()-2], y = vc[v][vc[v].size()-1];
    		if((f(x)-f(y)) * (X(x)-X(id)) >= (f(x)-f(id)) * (X(x)-X(y))) vc[v].pop_back(); 
    		else break;
    	}
    	vc[v].push_back(id);
    }
    int ans = 4e16;
    signed main(){
    	n = read(), m = read(), A = read(), B = read(), C = read();
    	for(int i = 1 ; i <= m ; i ++){
    		int u = read(), v = read(), p = read(), q = read();
    		e[i] = (Edge){u,v,p,q,i};
    	}
    	sort(e+1,e+m+1,sotcmp);
    	vc[1].push_back(0);
    	dp[0] = 0;
    	while(!pq.empty()) pq.pop();
    	for(int i = 1 ; i <= m ; i ++){
    		int u = e[i].u, v = e[i].v, p = e[i].p, q = e[i].q; e[i].id = i;
    		while(!pq.empty() && pq.top().q <= p) push(pq.top()), pq.pop();
    		while(vc[u].size() >= 2){
    			int x = vc[u][0], y = vc[u][1];
    			if(1.0*(f(x)-f(y))/(X(x)-X(y)) <= 2*A*p) vc[u].erase(vc[u].begin());
    			else break;
    		}
    		if(vc[u].size()){
    			int j = vc[u][0];
    			dp[i] = dp[j] + A*(e[i].p-e[j].q)*(e[i].p-e[j].q) + B*(e[i].p-e[j].q) + C;
    			pq.push(e[i]);
    			if(v == n) ans = min(ans,dp[i]+q);
    		}
    	}
    	printf("%lld",ans);
    }
    
  • 相关阅读:
    Selenium 元素定位方法小结
    java OpenCV挑战极验滑动拼图验证码
    使用java + selenium + OpenCV破解腾讯防水墙滑动验证码
    Java轻松破解顶象滑动拼图验证码
    Java中的chromedriver把Selenium的焦点转移到新窗口
    java 破解滑动拼图验证码(opencv+chromedriver+selenium )
    selenium+java破解极验滑块验证码
    Appium 本地编译 chromedriver 历程记录
    关闭 ChromeDriver was started successfully 提示信息
    Selenium执行完毕未关闭chromedriver/geckodriver进程的解决办法(java版)
  • 原文地址:https://www.cnblogs.com/Shinomiya/p/15464671.html
Copyright © 2011-2022 走看看