题目描述
小 \(A\) 和小 \(B\) 决定利用假期外出旅行,他们将想去的城市从 \(1\) 到 \(N\) 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 \(i\) 的海拔高度为 \(H_i\),城市 \(i\) 和城市 \(j\)之间的距离 \(d[i,j]\) 恰好是这两个城市海拔高度之差的绝对值,即 \(d[i,j]=∣H_i−H_j∣\) 。
旅行过程中,小 \(A\) 和小 \(B\) 轮流开车,第一天小 \(A\) 开车,之后每天轮换一次。他们计划选择一个城市 \(S\) 作为起点,一直向东行驶,并且最多行驶 \(X\) 公里就结束旅行。小 \(A\) 和小 \(B\) 的驾驶风格不同,小 \(B\) 总是沿着前进方向选择一个最近的城市作为目的地,而小 \(A\) 总是沿着前进方向选择第二近的城市作为目的地(注意:本题中如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出 \(X\) 公里,他们就会结束旅行。
在启程之前,小 \(A\) 想知道两个问题:
- 对于一个给定的 \(X=X_0\),从哪一个城市出发,小 \(A\) 开车行驶的路程总数与小 \(B\) 行驶的路程总数的比值最小(如果小 \(B\) 的行驶路程为 \(0\),此时的比值可视为无穷大,且两个无穷大视为相等)。如果从多个城市出发,小 \(A\) 开车行驶的路程总数与小 \(B\) 行驶的路程总数的比值都最小,则输出海拔最高的那个城市。
- 对任意给定的 \(X=X_i\) 和出发城市 \(S_i\),小 \(A\) 开车行驶的路程总数以及小 \(B\) 行驶的路程总数。
输入输出格式
输入格式:
第一行包含一个整数 \(N\),表示城市的数目。
第二行有 \(N\) 个整数,每两个整数之间用一个空格隔开,依次表示城市 \(1\) 到城市 \(N\) 的海拔高度,即 \(H_1,H_2,…,H_n\),且每个 \(H_i\) 都是不同的。
第三行包含一个整数 \(X_0\) 。
第四行为一个整数 \(M\),表示给定 \(M\) 组 \(S_i\) 和 \(X_i\) 。
接下来的 \(M\) 行,每行包含 \(2\) 个整数 \(S_i\) 和 \(X_i\),表示从城市 \(S_i\) 出发,最多行驶 \(X_i\) 公里。
输出格式:
输出共 \(M+1\) 行。
第一行包含一个整数 \(S_0\),表示对于给定的 \(X_0\),从编号为 \(S_0\) 的城市出发,小 \(A\) 开车行驶的路程总数与小 \(B\) 行驶的路程总数的比值最小。
接下来的 \(M\) 行,每行包含 \(2\) 个整数,之间用一个空格隔开,依次表示在给定的 \(S_i\) 和 \(X_i\)下小 \(A\) 行驶的里程总数和小 \(B\) 行驶的里程总数。
输入输出样例
输入样例#1:
4
2 3 1 4
3
4
1 3
2 3
3 3
4 3
输出样例#1:
1
1 1
2 0
0 0
0 0
输入样例#2:
10
4 5 6 1 2 3 7 8 9 10
7
10
1 7
2 7
3 7
4 7
5 7
6 7
7 7
8 7
9 7
10 7
输出样例#2:
2
3 2
2 4
2 1
2 4
5 1
5 1
2 1
2 0
0 0
0 0
分析
方法:倍增+\(DP\)+双向链表
这道题主要难在预处理部分,需要用到倍增。
后面的第一个问题,我们直接暴力枚举每一个起点即可。
至于路程改怎么算,用\(DP\)就好了。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define il inline
#define re register
#define maxn 100007
#define mymax(a,b) a>b?a:b
#define mymin(a,b) a<b?a:b
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &x){
T f=1;x=0;char c;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=x*10+(c^48);
x*=f;
}
struct city{
int l,r,h,id;
bool operator < (const city &a)const{return h<a.h;}
}tr[maxn];
int n,m,x,s;
int ans,disa,disb,flag;
int pre[maxn],mina[maxn],minb[maxn];
int f[25][maxn][2],fa[25][maxn][2],fb[25][maxn][2];
bool jud(int l,int r,int i){
if(!l) return 0;
if(!r) return 1;
return tr[i].h-tr[l].h<=tr[r].h-tr[i].h;
}
int find(int l,int r,int i){
if(!l) return tr[r].id;
if(!r) return tr[l].id;
if(tr[i].h-tr[l].h<=tr[r].h-tr[i].h) return tr[l].id;
else return tr[r].id;
}
void prepare(){
int j,l,r;
for(int i=1;i<=n;++i){
j=pre[i],l=tr[j].l,r=tr[j].r;
if(jud(l,r,j)) mina[i]=find(tr[l].l,r,j),minb[i]=tr[l].id;
else mina[i]=find(l,tr[r].r,j),minb[i]=tr[r].id;
if(l) tr[l].r=r;
if(r) tr[r].l=l;
}
}
void init(){
for(int i=1;i<=n;++i){
if(mina[i]) f[0][i][0]=mina[i],fa[0][i][0]=abs(tr[pre[i]].h-tr[pre[mina[i]]].h);
if(minb[i]) f[0][i][1]=minb[i],fb[0][i][1]=abs(tr[pre[i]].h-tr[pre[minb[i]]].h);
fa[0][i][1]=fb[0][i][0]=0;
}
for(int i=1;i<=20;++i)
for(int j=1;j<=n;++j)
for(int k=0;k<2;++k){
if(i==1) flag=k^1;
else flag=k;
if(f[i-1][j][k]) f[i][j][k]=f[i-1][f[i-1][j][k]][flag];
if(f[i][j][k]) {
fa[i][j][k]=fa[i-1][j][k]+fa[i-1][f[i-1][j][k]][flag];
fb[i][j][k]=fb[i-1][j][k]+fb[i-1][f[i-1][j][k]][flag];
}
}
}
void solve(int xi,int pos){
disa=disb=flag=0;
for(int i=20;i>=0;--i)
if(f[i][pos][flag]&&disa+disb+fa[i][pos][flag]+fb[i][pos][flag]<=xi){
disa+=fa[i][pos][flag],disb+=fb[i][pos][flag];
if(!i) flag^=1;
pos=f[i][pos][flag];
}
}
int main(){
read(n);
for(int i=1;i<=n;++i) read(tr[i].h),tr[i].id=i;
sort(tr+1,tr+1+n);
for(int i=1;i<=n;++i) pre[tr[i].id]=i,tr[i].l=i-1,tr[i].r=i+1;
tr[n].r=0;
prepare();
init();
read(x),read(m);
double minn=1000000001.0;
for(int i=1;i<=n;++i){
solve(x,i);
if(disb&&1.0*disa/disb<minn){
minn=1.0*disa/disb;
ans=i;
}
}
printf("%d\n",ans);
while(m--){
read(s),read(x);
solve(x,s);
printf("%d %d\n",disa,disb);
}
return 0;
}