[NOIP 2012] 开车旅行

题意:

传送门

题解:

set+倍增。

这题是一个高效模拟题,所以一般都要用一些比较高效的算法,小A和小B的路线都是固定的,所以很容易想到倍增来高效查询路线。

那么设:
g[i][j]表示从i开始往后\(2^j\)轮,车开到的城市。
f[i][j][0]表示从i开始往后\(2^j\)轮,小A开车的路程。
f[i][j][1]表示从i开始往后\(2^j\)轮,小B开车的路线。

首先预处理出走一轮的路线,这个可以用set来实现。

然后倍增转移。

对于第一问,直接暴枚每个点为起点,比较一下即可。

对于第二问,同理直接查询。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<set>
#define ll long long
#define N 100010
using namespace std;

int h[N],g[N][22],f[N][22][2],dis[N][2],to[N][2];

set<int> q;
map<int,int> mp;

int gi() {
  int x=0,o=1; char ch=getchar();
  while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
  if(ch=='-') o=-1,ch=getchar();
  while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
  return o*x;
}

void update(int x, int y) {
  int a=mp[x],b=mp[y],d=abs(x-y);
  if(!to[a][0] || d<dis[a][0] || (d==dis[a][0] && y<h[to[a][0]])) {
    to[a][1]=to[a][0],dis[a][1]=dis[a][0];
    to[a][0]=b,dis[a][0]=d;
  }
  else if(!to[a][1] || d<dis[a][1] || (d==dis[a][1] && y<h[to[a][0]])) {
    to[a][1]=b,dis[a][1]=d;
  }
}

void query(int x, int len, ll &a, ll &b) {
  for(int i=20; i>=0; i--) {
    if(g[x][i] && f[x][i][0]+f[x][i][1]<=len) {
      a+=f[x][i][0];
      b+=f[x][i][1];
      len-=f[x][i][0]+f[x][i][1];
      x=g[x][i];
    }
  }
  if(to[x][1] && dis[x][1]<=len) a+=dis[x][1]; 
}

int main() {
  int n=gi(),m,x,len,ans;
  ll ansa=0,ansb=0,a,b;
  for(int i=1; i<=n; i++) mp[h[i]=gi()]=i;
  for(int i=n; i>=1; i--) {
    q.insert(h[i]);
    set<int>::iterator it;
    it=q.find(h[i]);
    if(it!=q.begin()) {
      it--;
      update(h[i],*it);
      if(it!=q.begin()) {it--,update(h[i],*it),it++;}
      it++;
    }
    if((++it)!=q.end()) {
      update(h[i],*it);
      if((++it)!=q.end()) update(h[i],*it);
    }
  }
  for(int i=1; i<=n; i++) {
    g[i][0]=to[to[i][1]][0];
    f[i][0][0]=dis[i][1];
    f[i][0][1]=dis[to[i][1]][0];
  }
  for(int j=1; j<=20; j++)
    for(int i=1; i<=n; i++) {
      g[i][j]=g[g[i][j-1]][j-1];
      f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];
      f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];
    }
  len=gi();
  for(int i=1; i<=n; i++) {
    a=0,b=0;
    query(i,len,a,b);
    if(b && (!ansb || a*ansb<b*ansa || (a*ansb==b*ansa && h[i]>h[ans]))) ans=i,ansa=a,ansb=b;
  }
  printf("%d\n", ans);
  m=gi();
  while(m--) {
    x=gi(),len=gi(),a=0,b=0;
    query(x,len,a,b);
    printf("%lld %lld\n", a,b);
  }
  return 0;
}
posted @ 2017-11-09 13:47  HLX_Y  阅读(242)  评论(0编辑  收藏  举报