JZOJ3101. NOIP2012提高组 开车旅行

5 篇文章 0 订阅
1 篇文章 0 订阅

题目大意

给定 n 个城市,每个城市有一个海拔Hi定义距离 di,j=|HiHj|
A和B轮流开车,只能从编号小的到编号大的。每次A会从当前这一位置开到第二近的下一位置,B会从当前开到最近的下一位置。
当他们行驶的总路程超过 X 时,他们就会停止旅行。
现在有两个问题。

  1. 给定一个X,询问从哪个点出发,最终A走的路程与B路程的比值最小。

    • m 个询问,每个询问要求从给定起点Si出发,不超过 Xi 是,AB分别行驶的路程。
    • Data Constraint
      N100000,m10000

      题解

      因为我们需要尽可能逼近最终停下的位置,所以可以考虑倍增。
      Next[i][j] 表示从 i 出发,最终停下的位置。f[i][j]表示从 i 出发,走2j轮,A行驶的路程; g[i][j] 类似。
      然后用set预处理出每个位置AB分别会怎么走。这样一来 f,g 就很好求了。
      对于第一个问题,枚举起点然后倍增。
      第二个问题就直接倍增。

      时间复杂度: O(nlogn)

      SRC

      #include<cstdio>
      #include<cstdlib>
      #include<cstring>
      #include<iostream>
      #include<algorithm>
      #include<set>
      using namespace std ;
      
      #define N 100000 + 10
      typedef long long ll ;
      const int inf = 0x7FFFFFFF ;
      const int MAXN = 18 ;
      struct Note {
          int v , h ;
          Note ( int V = 0 , int H = 0 ) { v = V , h = H ; }
      } ;
      
      bool operator < ( Note a , Note b ) { return ( a.v < b.v || ( a.v == b.v && a.h < b.h ) ) ; }
      bool operator == ( Note a , Note b ) { return a.v == b.v && a.h == b.h ; }
      
      set < Note > Q ;
      
      int H[N] , NextA[N] , NextB[N] ;
      ll f[N][MAXN] , g[N][MAXN] , Next[N][MAXN] ;
      int n , m , X0 , wz , del ;
      long double q , ans = 1e15 ;
      ll dis1 , dis2 ;
      
      long double fabs( long double x ) { return x < 0 ? -x : x ; }
      
      bool cmp( Note a , Note b ) {
          if ( a.h == -inf || a.h == inf || a.h == -inf + 1 || a.h == inf - 1 ) return 0 ;
          if ( b.h == inf || b.h == -inf || b.h == -inf + 1 || b.h == inf - 1 ) return 1 ;
          if ( a.v < b.v ) return 1 ;
          if ( a.v == b.v && a.h < b.h ) return 1 ;
          return 0 ;
      }
      
      Note Smin( Note a , Note b ) {
          if ( cmp( Note( abs(a.v-del) , a.v ) , Note( abs(b.v-del) , b.v ) ) ) return a ;
          return b ;
      }
      
      void Pre() {
          Q.insert( Note( inf , 0 ) ) , Q.insert( Note( -inf , 0 ) ) ;
          Q.insert( Note( inf - 1 , 0 ) ) , Q.insert( Note( -inf + 1 , 0 ) ) ;
          for (int i = n ; i >= 1 ; i -- ) {
              del = H[i] ;
              set < Note > :: iterator it = Q.upper_bound( H[i] ) ;
              Note a = *it ;
              it ++ ;
              Note b = *it ;
              it -- ;
              it -- ;
              Note c = *it ;
              it -- ;
              Note d = *it ;
              Note miN = Smin( Smin(a,b) , Smin(c,d) ) ;
              if ( miN == a ) a = Note( inf , 0 ) ;
              if ( miN == b ) b = Note( inf , 0 ) ;
              if ( miN == c ) c = Note( inf , 0 ) ;
              if ( miN == d ) d = Note( inf , 0 ) ;
              NextB[i] = miN.h ;
              miN = Smin( Smin(a,b) , Smin(c,d) ) ;
              NextA[i] = miN.h ;
              Q.insert( Note( H[i] , i ) ) ;
          }
          for (int i = 1 ; i <= n ; i ++ ) Next[i][0] = NextB[NextA[i]] , f[i][0] = abs( H[i] - H[NextA[i]] ) , g[i][0] = abs( H[NextA[i]] - H[NextB[NextA[i]]] ) ;
          for (int j = 1 ; j < MAXN ; j ++ ) {
              for (int i = 1 ; i <= n ; i ++ ) {
                  Next[i][j] = Next[Next[i][j-1]][j-1] ;
                  f[i][j] = f[i][j-1] + f[Next[i][j-1]][j-1] ;
                  g[i][j] = g[i][j-1] + g[Next[i][j-1]][j-1] ;
              }
          }
      }
      
      void Find( int st , int lim ) {
          dis1 = 0 , dis2 = 0 ;
          for (int i = MAXN - 1 ; i >= 0 ; i -- ) {
              if ( f[st][i] + g[st][i] + dis1 + dis2 <= lim ) {
                  dis1 += f[st][i] ;
                  dis2 += g[st][i] ;
                  st = Next[st][i] ;
              }
          }
          if ( f[st][0] + dis1 + dis2 <= lim ) dis1 += f[st][0] ;
          if ( dis2 == 0 ) q = 1e14 ;
          else q = (long double)dis1 / dis2 ;
      }
      
      int main() {
          scanf( "%d" , &n ) ;
          for (int i = 1 ; i <= n ; i ++ ) scanf( "%d" , &H[i] ) ;
          H[0] = inf ;
          Pre() ;
          scanf( "%d" , &X0 ) ;
          for (int i = 1 ; i <= n ; i ++ ) {
              Find( i , X0 ) ;
              if ( q < ans ) {
                  ans = q ;
                  wz = i ;
              } else if ( H[i] > H[wz] && fabs(ans - q) <= 1e-8 ) wz = i ;
          }
          printf( "%d\n" , wz ) ;
          scanf( "%d" , &m ) ;
          for (int i = 1 ; i <= m ; i ++ ) {
              int st , X ;
              scanf( "%d%d" , &st , &X ) ;
              Find( st , X ) ;
              printf( "%lld %lld\n" , dis1 , dis2 ) ;
          }
          return 0 ;
      }
      

      以上.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值