解题思路
预处理最小点和次小点:
我们用双向链表。l[i]表示在原序列中第i个点排序后左边的点,r[i]表示在原序列中第i个点排序后右边的点。
先不管方向,将所有城市排序之后,它的最小和次小点一定在
l
[
i
]
,
l
[
l
[
i
]
]
,
r
[
i
]
,
r
[
r
[
i
]
]
l[i],l[l[i]],r[i],r[r[i]]
l[i],l[l[i]],r[i],r[r[i]]的位置,所以说如果排序我们的处理会方便很多。
首先我们记下每个城市排序之后的位置,然后从第一个城市开始向 l [ i ] , l [ l [ i ] ] , r [ i ] , r [ r [ i ] ] l[i],l[l[i]],r[i],r[r[i]] l[i],l[l[i]],r[i],r[r[i]]处找点。很显然,由于是第一个点,这个时候找到的任何点一定在它东边。同样的,找完第一个点后将第一个点删除,那么第二个点自然成为了第一个点,同理。
DP:
不难发现,每个点出发,先由小A/小B开车,开若干次车。一定是有一个唯一的终点,而且最多开n次车旅行就结束了。所以我们可以考虑倍增,令
f
0
/
1
,
i
,
j
f_{0/1,i,j}
f0/1,i,j表示从城市i出发,由小A/小B先开车,开2^j次车到达的终点,
d
i
s
0
/
1
,
i
,
j
dis_{0/1,i,j}
dis0/1,i,j表示从城市
i
i
i出发,由小A先开车,开
2
j
2^j
2j车,小A/小B开车的路程。
(因为 2 0 = 1 2^0=1 20=1是奇数,因此在转移到 2 2 2^2 22 时,前后两半时间先开车的人不一样,因此要独立出来转移;其余的状态转移方程均相同。
转移方程:
- f 0 , i , j = f 0 , f 0 , i , j − 1 , j − 1 f_{0,i,j}=f_{0,f_{0,i,j-1},j-1} f0,i,j=f0,f0,i,j−1,j−1
- d i s 0 , i , j = d i s 0 , i , j − 1 + d i s 0 , f 0 , i , j − 1 , j − 1 dis_{0,i,j}=dis_{0,i,j-1}+dis_{0,f_{0,i,j-1},j-1} dis0,i,j=dis0,i,j−1+dis0,f0,i,j−1,j−1
- d i s 1 , i , j = d i s 1 , i , j − 1 + d i s 1 , f 0 , i , j − 1 , j − 1 dis_{1,i,j}=dis_{1,i,j-1}+dis_{1,f_{0,i,j-1},j-1} dis1,i,j=dis1,i,j−1+dis1,f0,i,j−1,j−1
特别的,当 j = 1 j=1 j=1有:
- f 0 , i , 1 = f 1 , f 0 , i , 0 , 0 f_{0,i,1}=f_{1,f_{0,i,0},0} f0,i,1=f1,f0,i,0,0
- d i s 0 , i , 1 = d i s 0 , i , 0 dis_{0,i,1}=dis_{0,i,0} dis0,i,1=dis0,i,0
- d i s 1 , i , 1 = d i s 1 , f 0 , i , 0 , 0 dis_{1,i,1}=dis_{1,f_{0,i,0},0} dis1,i,1=dis1,f0,i,0,0
PS:倍增的初始化要注意的的地方。
对于f,记得一定是2^j个两步之后所到达的点,与A,B谁开车无关。
对于B,由于开始一定是A先跑,所以我们要用次小点的最小点之间的距离初始化。
求解问题1:
很显然,要知道小A和小B行驶路程的比值,只要知道他们分别行驶的路程就可以了。这里定义一个函数
f
i
n
d
(
S
,
X
)
find(S,X)
find(S,X)求解从城市S出发,最多行驶X公里,小A和小B分别行驶了多少距离。
通过上面DP推出的三个数组,函数的实现就不难了,只要倍增模拟前进即可。
求解问题2就更明显了,直接多次求解
f
i
n
d
(
s
i
,
x
i
)
find(s_i,x_i)
find(si,xi)即可,这里就不再赘述。
代码
调了超级九。。。枯了。。。
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const long long INF=0x7ffffffffffff;
int n,s0,m,s[100010],l[100010],r[100010],f[2][100010][20];
long long dis[2][100010][20],w[100010],la,lb,x1,x[100010];
double c=INF;
struct cc {
long long x;
int id;
} h[100010];
bool cmp(cc l,cc r) {
return l.x<r.x;
}
void find(long long s,long long x1) {
long long s1=s;
la=0,lb=0;
for(int j=18; j>=0; j--) {
if(f[0][s1][j]&&x1>=dis[0][s1][j]+dis[1][s1][j]) {
la+=dis[0][s1][j],lb+=dis[1][s1][j];
x1-=dis[0][s1][j]+dis[1][s1][j];
s1=f[0][s1][j];
}
}
return;
}
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++) {
scanf("%lld",&w[i]);
h[i].x=w[i];
h[i].id=i;
}
scanf("%lld%d",&x1,&m);
for(int i=1; i<=m; i++)
scanf("%d%lld",&s[i],&x[i]);
sort(h+1,h+n+1,cmp);
for(int i=1; i<=n; i++) {
l[h[i].id]=h[i-1].id;
r[h[i].id]=h[i+1].id;
}
for(int i=1; i<=n; i++) {
long long a=INF,b=INF,st[5];
int aa=0,bb=0;
if(l[i])st[1]=w[i]-w[l[i]];
else st[1]=INF;
if(r[i])st[2]=w[r[i]]-w[i];
else st[2]=INF;
if(l[l[i]])st[3]=w[i]-w[l[l[i]]];
else st[3]=INF;
if(r[r[i]])st[4]=w[r[r[i]]]-w[i];
else st[4]=INF;
for(int j=1; j<=2; j++)
if(st[j]<a)
a=st[j],aa=j;
if(aa==1)f[1][i][0]=l[i];
if(aa==2)f[1][i][0]=r[i];
for(int j=1; j<=4; j++)
if(j!=aa&&st[j]<b)
b=st[j],bb=j;
if(bb==1)f[0][i][0]=l[i];
if(bb==2)f[0][i][0]=r[i];
if(bb==3)f[0][i][0]=l[l[i]];
if(bb==4)f[0][i][0]=r[r[i]];
if(f[0][i][0])dis[0][i][0]=b;
if(f[1][i][0])dis[1][i][0]=a;
if(l[i])r[l[i]]=r[i];
if(r[i])l[r[i]]=l[i];
}
for(int i=1; i<=n; i++) {
f[0][i][1]=f[1][f[0][i][0]][0];
dis[0][i][1]=dis[0][i][0];
dis[1][i][1]=dis[1][f[0][i][0]][0];
}
for(int i=1; i<=n; i++)
dis[1][i][0]=0;
for(int j=2; j<=18; j++) {
for(int i=1; i<=n; i++) {
f[0][i][j]=f[0][f[0][i][j-1]][j-1];
dis[0][i][j]=dis[0][i][j-1]+dis[0][f[0][i][j-1]][j-1];
dis[1][i][j]=dis[1][i][j-1]+dis[1][f[0][i][j-1]][j-1];
}
}
for(int i=1; i<=n; i++) {
find(i,x1);
double cc=(double)la/(double)lb;
if(cc<c) {
c=cc;
s0=i;
} else if(cc==c&&w[i]>w[s0])
s0=i;
}
printf("%lld\n",s0);
for(int i=1; i<=m; i++) {
find(s[i],x[i]);
printf("%lld %lld\n",la,lb);
}
}