蓝书(算法竞赛进阶指南)刷题记录——CH5701 & 洛谷1081 【NOIP2012】开车旅行(双向链表+倍增)

题目:CH5701/luogu1081).
题目大意:给定一个长度为 n n 的序列 h h ,定义序列上两个位置 i , j i,j 的距离 d i s ( i , j ) h i h j dis(i,j)-|h_i-h_j| .现在有两个人驾驶一辆车从一个位置 s s 开始,B每次挑选当前位置之后最近的位置驾驶,A每次挑选当前位置之后第二近的位置驾驶(如果两个位置到当前位置的距离相同,则 h h 值更小的更近),A,B轮流驾驶且A先驾驶.现在有两个问题:
1.给定 x 0 x_0 ,表示两人走的距离之和不超过 x 0 x_0 ,求一个 h h 最大的位置,使得A行走的距离与B行走的距离的比值最大(若B驾驶的距离为 0 0 ,则比值为 + +\infty ).
2.给定 x i , s i ( 1 i m ) x_i,s_i(1\leq i\leq m) ,表示从 s i s_i 开始走不超过 x i x_i 的距离,输出A驾驶的距离和 B B 驾驶的距离.

1 n , m 1 0 5 1\leq n,m\leq 10^5 .

虽然蓝书说是倍增优化DP,但我感觉这题跟DP好像并没有什么关系…

先考虑当前在位置 p p 上时,A / / B驾驶一次会有多长的距离,到达哪一个点.

很明显这是个双向链表就可以解决的问题.先给 h h 拍个序并链成链表,求位置 i i 的时候找一下 i i 的前驱和后继中大的即为B的答案,A的答案再找一下前驱的前驱(B的答案为前驱时)或后继的后继(B的答案为后继时)与原来前驱与后继中的较小值比较一下即可.最后注意把位置 i i 从双向链表中删除.

我们再思考一个问题,从某一个位置 s s 出发,行驶路径是不是确定的呢?显然是的.

既然一条路径是确定的,是不是就可以用倍增来预处理一下从每个点出发行驶 2 i 2^i 次A / / B行驶的距离了呢?显然这也是可以的.

那么整道题的思路就很明显了,直接用倍增跑就好了.第一个问题可以 O ( n log n ) O(n\log n) ,第二个问题可以 O ( m log n ) O(m\log n) 搞了.

不过这个倍增有一个问题,在倍增的时候只有 i 1 i\leq 1 的时候才一定是从A开始到B结束,下一次又从A开始的,所以我们必须在最后特判一下 2 0 2^0 的情况.

总时间复杂度为 O ( ( n + m ) log n ) O((n+m)\log n) .

代码如下:

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

#define Abigail inline void
typedef long long LL;

const int N=100000,C=20,INF=(1<<30)-1;
const double eps=1e-7;

int n;
struct city{
  int h,id,next,last;
}d[N+9];

bool cmp1(const city &a,const city &b){return a.h<b.h||a.h==b.h&&a.id<b.id;}
bool cmp2(const city &a,const city &b){return a.id<b.id;}

void Pre_list(){
  sort(d+1,d+n+1,cmp1);
  for (int i=1;i<=n;++i)
    d[i].next=d[i+1].id,d[i].last=d[i-1].id;
  sort(d+1,d+n+1,cmp2);
  d[0].h=INF<<1;
}

void Erase(int k){
  if (d[k].next) d[d[k].next].last=d[k].last;
  if (d[k].last) d[d[k].last].next=d[k].next;
}

int Get_dis(int x,int y){return abs(d[x].h-d[y].h);}

int pdis[N+9][2],pgo[N+9][2];

void Pre_dis(){
  pdis[0][0]=pdis[0][1]=INF;
  for (int i=1;i<=n;++i){
    int tl=Get_dis(i,d[i].last),tr=Get_dis(i,d[i].next),t;
    if (tl<=tr){
      pdis[i][0]=tl;pgo[i][0]=d[i].last;
      t=Get_dis(i,d[d[i].last].last);
      if (t<=tr) pdis[i][1]=t,pgo[i][1]=d[d[i].last].last;
      else pdis[i][1]=tr,pgo[i][1]=d[i].next;
	}else{
	  pdis[i][0]=tr;pgo[i][0]=d[i].next;
	  t=Get_dis(i,d[d[i].next].next);
	  if (tl<=t) pdis[i][1]=tl,pgo[i][1]=d[i].last;
	  else pdis[i][1]=t,pgo[i][1]=d[d[i].next].next;
	}
	pdis[i][0]=min(pdis[i][0],INF);
	pdis[i][1]=min(pdis[i][1],INF);
	Erase(i);
  }
}

int dis[N+9][C][2],go[N+9][C];

void Pre_doubly(){
  for (int i=1;i<=n;++i){
    dis[i][1][1]=pdis[i][1];
    dis[i][1][0]=pdis[pgo[i][1]][0];
	go[i][1]=pgo[pgo[i][1]][0];
  } 
  for (int i=2;i<C;++i){
  	for (int j=1;j<=n;++j){
  	  dis[j][i][0]=min(INF,dis[j][i-1][0]+dis[go[j][i-1]][i-1][0]);
  	  dis[j][i][1]=min(INF,dis[j][i-1][1]+dis[go[j][i-1]][i-1][1]);
  	  go[j][i]=go[go[j][i-1]][i-1];
	}
  }
}

void Doubly_dis(int p,int x,int &ans0,int &ans1){
  ans0=ans1=0;
  for (int i=C-1;i>=1;--i)
    if (ans0+dis[p][i][0]+ans1<=x-dis[p][i][1]){
      ans0+=dis[p][i][0];ans1+=dis[p][i][1];
      p=go[p][i];
	}
  if (ans0+ans1+pdis[p][1]<=x) ans1+=pdis[p][1];
}

int m,x[N+9],s[N+9],ans[N+9][2];

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i){
    scanf("%d",&d[i].h);
    d[i].id=i;
  }
  scanf("%d",&x[0]);
  scanf("%d",&m);
  for (int i=1;i<=m;++i)
    scanf("%d%d",&s[i],&x[i]);
}

Abigail work(){
  Pre_list();
  Pre_dis();
  Pre_doubly();
  Doubly_dis(s[0]=1,x[0],ans[0][0],ans[0][1]);
  double now=ans[0][1]*1.0/ans[0][0];
  for (int i=2;i<=n;++i){
  	Doubly_dis(i,x[0],ans[0][0],ans[0][1]);
    double t=ans[0][1]*1.0/ans[0][0];
    if (t<now) now=t,s[0]=i;
  }
  for (int i=1;i<=m;++i) Doubly_dis(s[i],x[i],ans[i][0],ans[i][1]);
}

Abigail outo(){
  printf("%d\n",s[0]);
  for (int i=1;i<=m;++i)
    printf("%d %d\n",ans[i][1],ans[i][0]);
}

int main(){
  into();
  work();
  outo();
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/92399637