题目:CH5701/luogu1081.
题目大意:给定一个长度为
n
n
n的序列
h
h
h,定义序列上两个位置
i
,
j
i,j
i,j的距离
d
i
s
(
i
,
j
)
−
∣
h
i
−
h
j
∣
dis(i,j)-|h_i-h_j|
dis(i,j)−∣hi−hj∣.现在有两个人驾驶一辆车从一个位置
s
s
s开始,B每次挑选当前位置之后最近的位置驾驶,A每次挑选当前位置之后第二近的位置驾驶(如果两个位置到当前位置的距离相同,则
h
h
h值更小的更近),A,B轮流驾驶且A先驾驶.现在有两个问题:
1.给定
x
0
x_0
x0,表示两人走的距离之和不超过
x
0
x_0
x0,求一个
h
h
h最大的位置,使得A行走的距离与B行走的距离的比值最大(若B驾驶的距离为
0
0
0,则比值为
+
∞
+\infty
+∞).
2.给定
x
i
,
s
i
(
1
≤
i
≤
m
)
x_i,s_i(1\leq i\leq m)
xi,si(1≤i≤m),表示从
s
i
s_i
si开始走不超过
x
i
x_i
xi的距离,输出A驾驶的距离和
B
B
B驾驶的距离.
1 ≤ n , m ≤ 1 0 5 1\leq n,m\leq 10^5 1≤n,m≤105.
虽然蓝书说是倍增优化DP,但我感觉这题跟DP好像并没有什么关系…
先考虑当前在位置 p p p上时,A / / /B驾驶一次会有多长的距离,到达哪一个点.
很明显这是个双向链表就可以解决的问题.先给 h h h拍个序并链成链表,求位置 i i i的时候找一下 i i i的前驱和后继中大的即为B的答案,A的答案再找一下前驱的前驱(B的答案为前驱时)或后继的后继(B的答案为后继时)与原来前驱与后继中的较小值比较一下即可.最后注意把位置 i i i从双向链表中删除.
我们再思考一个问题,从某一个位置 s s s出发,行驶路径是不是确定的呢?显然是的.
既然一条路径是确定的,是不是就可以用倍增来预处理一下从每个点出发行驶 2 i 2^i 2i次A / / /B行驶的距离了呢?显然这也是可以的.
那么整道题的思路就很明显了,直接用倍增跑就好了.第一个问题可以 O ( n log n ) O(n\log n) O(nlogn),第二个问题可以 O ( m log n ) O(m\log n) O(mlogn)搞了.
不过这个倍增有一个问题,在倍增的时候只有 i ≤ 1 i\leq 1 i≤1的时候才一定是从A开始到B结束,下一次又从A开始的,所以我们必须在最后特判一下 2 0 2^0 20的情况.
总时间复杂度为 O ( ( n + m ) log n ) O((n+m)\log n) O((n+m)logn).
代码如下:
#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;
}