loj2395 [JOISC 2017 Day 2]火车旅行

传送门

分析

我们知道无论往左走还是往右走一定都是往不低于这个点的地方走

于是我们可以考虑用倍增来维护一个点向左和向右走$2^i$最远分别能走到哪里

我们可以先用单调栈求出直走一步的情况,之后再处理倍增数组

值得注意的是有可能一直往左走不是最优情况,而先向右再想左会使答案更优

于是le[x][i]=min(le[le[x][i-1]][i-1],le[ri[x][i-1]][i-1])

向右的情况同理

于是我们进一步考虑如何通过倍增数组得到答案、

我们先从左面往右走,只要走$2^i$不会超过右节点则就让它走这么多步

一直走到不能再走之后再从右往左走

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
const int LOG = 18;
int a[100100],le[100100][LOG+3],ri[100100][LOG+3],st[100100],top;
int main(){
    int n,m,i,j,k;
    scanf("%d%d%d",&n,&k,&m);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    top=0;
    st[0]=1;
    for(i=1;i<=n;i++){
      while(top&&a[st[top]]<a[i])top--;
      le[i][0]=st[top];
      st[++top]=i;
    }
    top=0;
    st[0]=n;
    for(i=n;i>0;i--){
      while(top&&a[st[top]]<a[i])top--;
      ri[i][0]=st[top];
      st[++top]=i;
    }
    for(i=1;i<=LOG;i++)
      for(j=1;j<=n;j++){
        le[j][i]=min(le[le[j][i-1]][i-1],le[ri[j][i-1]][i-1]);
        ri[j][i]=max(ri[ri[j][i-1]][i-1],ri[le[j][i-1]][i-1]);
      }
    while(m--){
      int x,y,Ans=0,L,R,tl,tr;
      scanf("%d%d",&x,&y);
      if(x>y)swap(x,y);
      L=R=x;
      for(i=LOG;i>=0;i--){
          tl=min(le[L][i],le[R][i]);
          tr=max(ri[L][i],ri[R][i]);
          if(tr<y){
            L=tl,R=tr;
            Ans+=(1<<i);
          }
      }
      x=R;
      L=R=y;
      for(i=LOG;i>=0;i--){
          tl=min(le[L][i],le[R][i]);
          tr=max(ri[L][i],ri[R][i]);
          if(tl>x){
            L=tl,R=tr;
            Ans+=(1<<i);
          }
      }
      printf("%d\n",Ans);
    }
    return 0;
}
posted @ 2018-11-06 09:06  水题收割者  阅读(794)  评论(0编辑  收藏  举报