开车旅行(ST表)

题意

有两个人轮流开车,从起点S出发向东走,距离就是两个位置的高度差的绝对值,A喜欢第二近的地方,B喜欢最近的地方。他们会停止旅行当且仅当找不到下个满足条件的位置或者再走的话总路程会超过x。A先开车。

有两个问题:

1.对于给定的x,求在哪个起点出发,A与B路程比最小,disB=0的话视为无穷大,(oo=oo),相同的话取高度最高的位置。

2.给出m个询问,给出S,x,求A,B的行驶路程。

题解

首先考虑先求出A,B在每个位置出发的下个位置,给出一种做法(其实是抄的题解):先按高度排序,对于原序列的位置i,记排序后位置k,与他最近的肯定是k+1或者k-1的位置,这很显然,因为高度最接近嘛;那么第二近的同理应该是k-2,k-1,k+1,k+2中的一个,分类讨论一下即可。

但是问题来了,怎么保证找到的在原序列一定在他右边(也就是向东的条件),先模拟一遍题解过程,对于原序列第一个位置,其他位置都在他后面,所以他可以随便找,那么如果我们想要第二个也达到这样的效果,就要把第一个删掉,让第二个成为第一个。于是题解双向链表就来了,在排好序的序列,每个点都有两个指针,一个指向他右边的点,另一个指向左边的点,那么删除i位置就是让i左边指向的点的右指针不指他,而指向i右边指向的点,对于i右边指向的点同理。有点抽象,就相当于是忽略i这个位置。

那么现在已经找出了每个点A,B的走向,但是我们不可能去模拟整个走的过程,这样就是(n^2+nm)了,那怎么优化呢,A,B不同的人驾驶能合并吗。

好像我想多了,可以合并,利用f[i][j]表示从i出发A,B各开2^j次到达的地方,就可以走得飞快;

那么距离不超过x呢?一开始看成每个人走的不超过x,就以为不能合并,如果是全程的话应该还比较简单了吧,同理disa[i][j],disb[i][j]。

转移应该也还好,比较简单。

一开始照着题解写完init都没写在主函数调了半天(笑哭)。

分类讨论有点小恶心,还是写成了自己喜欢的风格,感觉自己更容易理解一些,清晰一些吧。

对于原序列的每个点,在求高度什么的要去排序后的序列找就需要记录一个排序后序列位置,不要弄混了或者忘了。

高度差的绝对值不要忘了。

第一个问题的分类过程有点小细节。

在模拟过程的时候倒着枚举,大概和树上倍增求lca差不多,能跳大的就跳大的,因为方案是确定的,所有两个人总共的天数也是确定的,那么对于这个天数一定可以进行二进制分解,要注意的是,这个天数是奇数就代表最后A多走一次,但是实际上天数未知,所以具体体现在代码就是for完之后从这个位置到下个A去的位置仍满足条件。

#include<bits/stdc++.h>
using namespace std;

#define ll long long
const double eps=1e-18;
const int maxn=100005;
int n,m;
int pos[maxn];//排序后的位置
int l[maxn],r[maxn];//链表
int fir[maxn],sec[maxn];
int st[maxn][25],lg[maxn];
int f[maxn][21];//f:从iA,B每人开2^J次
ll disa[maxn][21],disb[maxn][21];
ll a_d,b_d,ans;
double rate=1e18;
struct hill{
  int id,height;
}h[maxn];

template<class T>inline void read(T &x){
  x=0;int f=0;char ch=getchar();
  while(!isdigit(ch)) {f|=(ch==‘-‘);ch=getchar();}
  while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
  x= f ? -x : x ;
}

bool cmp(hill a,hill b){
  return a.height<b.height;
}

int get_h(int x,int y){
  return h[x].height-h[y].height;
}

void get(int x,int y){//排序前,排序后
  if(l[y]){
    if(!r[y]){
      fir[x]=h[l[y]].id;
      if(l[l[y]]) sec[x]=h[l[l[y]]].id;
    }
    else{
      if(get_h(y,l[y])<get_h(r[y],y)){
        fir[x]=h[l[y]].id;
        if(l[l[y]]&&get_h(y,l[l[y]])<=get_h(r[y],y)) sec[x]=h[l[l[y]]].id;
        else sec[x]=h[r[y]].id;
      }
      else if(get_h(y,l[y])==get_h(r[y],y)){
        fir[x]=h[l[y]].id;
        sec[x]=h[r[y]].id;
      }
      else {
        fir[x]=h[r[y]].id;
        if(r[r[y]]&&get_h(r[r[y]],y)<get_h(y,l[y])) sec[x]=h[r[r[y]]].id;
        else sec[x]=h[l[y]].id;
      }
    }
  }
  else {
    if(r[y]){
      fir[x]=h[r[y]].id;
      if(r[r[y]]) sec[x]=h[r[r[y]]].id;
    }
  }
  l[r[y]]=l[y];
  r[l[y]]=r[y];
}

void init(){
  for(int i=1;i<=n;i++){
    f[i][0]=fir[sec[i]];
    //printf("%d ",f[i][0]);
    disa[i][0]=abs(get_h(pos[i],pos[sec[i]]));
    disb[i][0]=abs(get_h(pos[sec[i]],pos[fir[sec[i]]]));
  }
  //putchar(10);
  for(int j=1;j<=20;j++)
   for(int i=1;i<=n;i++){
      f[i][j]=f[f[i][j-1]][j-1];
      disa[i][j]=disa[i][j-1]+disa[f[i][j-1]][j-1];
      //if(disa[i][j]<0) printf("hh\n");
      disb[i][j]=disb[i][j-1]+disb[f[i][j-1]][j-1];
   }
}

void get_it(ll x,int s){
  a_d=b_d=0;
  for(int j=20;~j;j--)
   if(f[s][j]&&a_d+b_d+disa[s][j]+disb[s][j]<=x){
     a_d+=disa[s][j];
     b_d+=disb[s][j];
     s=f[s][j];
   }
   if(sec[s]&&a_d+b_d+abs(get_h(pos[s],pos[sec[s]]))<=x) a_d+=abs(get_h(pos[s],pos[sec[s]]));
}

int main(){
  read(n);
  for(int i=1;i<=n;i++){
    read(h[i].height);
    h[i].id=i;
  }
  sort(h+1,h+n+1,cmp);
  for(int i=1;i<=n;i++){
    pos[h[i].id]=i;
    l[i]=i-1;r[i]=i+1;
  }
  //for(int i=1;i<=n;i++) printf("%d ",pos[i]);
  //putchar(10);
  r[n]=0;
  for(int i=1;i<=n;i++) get(i,pos[i]);
  init();
  ll x,s;
  read(x);
  for(int i=1;i<=n;i++){
    get_it(x,i);
    //printf("%lld %lld\n",a_d,b_d);
    if(!b_d){
      if(!ans) ans=i;
      else if(rate==1e18&&h[pos[i]].height>h[pos[ans]].height) ans=i;
    }
    else {
      if(rate*b_d>1.0*a_d){
        ans=i;
        rate=1.0*a_d/b_d;
      }
    }
  }
  printf("%lld\n",ans);
  read(m);
  for(int i=1;i<=m;i++){
    read(s);read(x);
    get_it(x,s);
    printf("%lld %lld\n",a_d,b_d);
  }
}

原文地址:https://www.cnblogs.com/sto324/p/11294530.html

时间: 2024-10-13 14:08:18

开车旅行(ST表)的相关文章

noip2012 开车旅行

P1081 开车旅行 139通过 484提交 题目提供者洛谷OnlineJudge 标签倍增2012NOIp提高组 难度提高+/省选- 提交该题 讨论 题解 记录 题目描述 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的 城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 Hi,城市 i 和城市 j 之间的距离 d[i,j]恰好是这两个城市海拔高度之差的绝对值,即 d[i,j] = |Hi− Hj|. 旅行过程中,小

[51nod 1288]汽油补给(ST表+单调栈)

[51nod 1288]汽油补给(ST表+单调栈) 题面 有(N+1)个城市,0是起点N是终点,开车从0 -> 1 - > 2...... -> N,车每走1个单位距离消耗1个单位的汽油,油箱的容量是T.给出每个城市到下一个城市的距离D,以及当地的油价P,求走完整个旅途最少的花费.如果无法从起点到达终点输出-1. 分析 贪心考虑,当我们到达一个城市x的时候,我们下一个到的城市应该是在x加满油的情况下,能到达的油价比x低的城市.如果每个加油城市之间的路都这样走,那么最后的价钱一定是最小的.

RMQ问题 - ST表的简单应用

2017-08-26 22:25:57 writer:pprp 题意很简单,给你一串数字,问你给定区间中最大值减去给定区间中的最小值是多少? 用ST表即可实现 一开始无脑套模板,找了最大值,找了最小值,分别用两个函数实现,实际上十分冗余 所以TLE了 之后改成一个函数中同时处理最大值和最小值,就可以了 AC代码如下: /* @theme:poj 3264 @writer:pprp @declare:ST表(sparse table)稀疏表,用动态规划的思想来解决RMQ问题: @date:2017

[模板]ST表浅析

ST表,稀疏表,用于求解经典的RMQ问题.即区间最值问题. Problem: 给定n个数和q个询问,对于给定的每个询问有l,r,求区间[l,r]的最大值.. Solution: 主要思想是倍增和区间dp. 状态:dp[i][j] 为闭区间[i,i+2^j-1]的最值. 这个状态与转移方程的关系很大,即闭区间的范围涉及到了转移方程的简便性. 转移方程:dp[i][j]=max(dp[i][j-1],dp[i+2^(j-1)][j-1]). 这是显然的,但这里有个细节:第一个项的范围为[i,i+2^

【模板】ST表 洛谷P1816 忠诚

P1816 忠诚 题目描述 老管家是一个聪明能干的人.他为财主工作了整整10年,财主为了让自已账目更加清楚.要求管家每天记k次账,由于 管家聪明能干,因而管家总是让财主十分满意.但是由于一些人的挑拨,财主还是对管家产生了怀疑.于是他决定用一种特别的方法来判断管家的忠诚,他把每次的 账目按1,2,3…编号,然后不定时的问管家问题,问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题. 输入输出格式 输入格式: 输入中第一行有两个数m,n表示有m(m<=100000

GCD(st表)

GCD Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 3432    Accepted Submission(s): 1227 Problem Description Give you a sequence of N(N≤100,000) integers : a1,...,an(0<ai≤1000,000,000). There a

UVA 11475 Extend to Palindrome(后缀数组+ST表)

[题目链接] http://acm.hust.edu.cn/vjudge/problem/27647 [题目大意] 给出一个字符串,要求在其后面添加最少的字符数,使得其成为一个回文串.并输出这个回文串. [题解] 用拼接符将原字符串倒序相接,做一遍后缀数组,查询两串相应位置的LCP就是以该点为中心的回文串长度的一半分,奇偶求出所有后缀回文串,保留最长的,则补充部分为剩下的前缀的倒置.至于查询两串的LCP我们可以在height数组建立ST表查询. [代码] #include <cstdio> #

lca最近公共祖先(st表)

大体思路 1.求出每个元素在树中的深度 2.用st表预处理的方法处理出f[i][j],f[i][j]表示元素i上方第2^j行对应的祖先是谁 3.将较深的点向上挪,直到两结点的深度相同 4.深度相同后,祖先可能就在上方,再走几步就到了,于是两个点同时向上移 具体的方法和代码贴在下面 ↓ 具体来看 1.求出每个元素在树中的深度 //求每个节点在树中的深度 void dfs(int pos,int pre)//pre是pos的父节点 { for(int i=0;i<v[pos].size;i++)//

【BZOJ-3956】Count ST表 + 单调栈

3956: Count Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 173  Solved: 99[Submit][Status][Discuss] Description Input Output Sample Input 3 2 0 2 1 2 1 1 1 3 Sample Output 0 3 HINT M,N<=3*10^5,Ai<=10^9 Source CH Round#64 MFOI杯水题欢乐赛day1 By Gromah So