开车旅行(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
");
      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
",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
",ans);
  read(m);
  for(int i=1;i<=m;i++){
    read(s);read(x);
    get_it(x,s);
    printf("%lld %lld
",a_d,b_d);
  }
}
View Code
原文地址:https://www.cnblogs.com/sto324/p/11294530.html