题意:
小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 11 到 NN 编号,且编号较小的城市在编号较大的城市的西边,
已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 $H_i$ ,城市 i 和城市 j 之间的距离 $d_{[i,j]}=|H_i-H_j|$ 。
旅行过程中,小 A 和小 B 轮流开车,第一天小 A 开车,之后每天轮换一次。
他们计划选择一个城市 SS 作为起点,一直向东行驶,并且最多行驶 X 公里就结束旅行。
小 A 和小 B 的驾驶风格不同,小 B 总是沿着前进方向选择一个最近的城市作为目的地,而小 A 总是沿着前进方向选择第二近的城市作为目的地
(注意:本题中如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。
如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出 X 公里,他们就会结束旅行。
在启程之前,小 A 想知道两个问题:
1:对于一个给定的 $X=X_0$ ,从哪一个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值最小(如果小 B 的行驶路程为 0 ,此时的比值可视为无穷大,且两个无穷大视为相等)。
如果从多个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值都最小,则输出海拔最高的那个城市。
2:对任意给定的 $X=X_i$ 和出发城市 $S_i$ ,小 A 开车行驶的路程总数以及小 B 行驶的路程总数。
以f[i][j]记录从i点经过$2^j$轮后他们所到的点
以disa[i][j]记录a从i点经过$2^j$天自己走的距离
以disb[i][j]记录b从i点经过$2^j$天自己走的距离
以ato[i]记录a从i点走一步所到的点
以bto[i]记录b从i点走一步所到的点
首先,把所有城市按海拔排序,并建立排序前后的映射
从第一个城市开始枚举,那么所有的城市都在它的东边
然后对应到排序好的城市中,处理出ato[i],bto[i]
处理完把当前城市删除,第二个变成第一个,因此再次求的时候仍然所有点都在东边
之后处理st表,模拟即可
#include<cstdio> #include<iostream> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define int long long #define olinr return #define _ 0 #define nmr 105050 #define love_nmr 0 #define DB double int n; struct node { int pre; int nxt; int h; int id; friend bool operator < (const node &a,const node &b) { return a.h<b.h; } }s[nmr]; int disa[nmr][31]; int disb[nmr][31]; int f[nmr][31]; int ato[nmr]; int bto[nmr]; int X; int m; int mp[nmr]; int ans; DB minn=0x7fffffff; inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-f; ch=getchar(); } while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void put(int x) { if(x<0) { x=-x; putchar('-'); } if(x>9) put(x/10); putchar(x%10+'0'); } inline int abss(int x) { if(x<0) return -x; return x; } inline bool near_left(int now,int l,int r) { if(!l) return false; if(!r) return true; int disl=abss(s[l].h-s[now].h); int disr=abss(s[r].h-s[now].h); if(disl<disr) return true; if(disl>disr) return false; return s[l].h<s[r].h; } inline int opposite(int now,int l,int r) { if(!l) return s[r].id; if(!r) return s[l].id; int disl=abss(s[l].h-s[now].h); int disr=abss(s[r].h-s[now].h); if(disl<disr) return s[l].id; if(disl>disr) return s[r].id; if(s[l].h<s[r].h) return s[l].id; return s[r].id; } inline void beizeng() { for(int i=1;i<=n;i++) //a---->b---->c { f[i][0]=bto[ato[i]]; //a---->c disa[i][0]=abss(s[mp[i]].h-s[mp[ato[i]]].h); //a---->b disb[i][0]=abss(s[mp[f[i][0]]].h-s[mp[ato[i]]].h); //b---->c } for(int j=1;j<=23;j++) for(int i=1;i<=n;i++) { f[i][j]=f[f[i][j-1]][j-1]; disa[i][j]=disa[i][j-1]+disa[f[i][j-1]][j-1]; disb[i][j]=disb[i][j-1]+disb[f[i][j-1]][j-1]; } } inline void running(int sta,int &a,int &b,int limit) { for(int i=22;i>=0;i--) { if(f[sta][i]&&(a+b+disa[sta][i]+disb[sta][i])<=limit) //模拟倍增过程,A、B一起跑 { a+=disa[sta][i]; b+=disb[sta][i]; sta=f[sta][i]; } } if(ato[sta]&&(a+b+disa[sta][0])<=limit) //现在AB同时跑不行,但可以看看A单独跑行不行!! a+=disa[sta][0]; } signed main() { n=read(); for(int i=1;i<=n;i++) { s[i].h=read(); s[i].id=i; } sort(s+1,s+n+1); for(int i=1;i<=n;i++) { s[i].pre=i-1; s[i].nxt=i+1; mp[s[i].id]=i; //排序前后映射 } s[n].nxt=s[1].pre=0; //排序后的左右 for(int i=1;i<=n;i++) { int pos=mp[i]; int l=s[pos].pre; //此时l,r都在当前的东边! int r=s[pos].nxt; if(near_left(pos,l,r)) //判断左边是否最近 { bto[i]=s[l].id; ato[i]=opposite(pos,s[l].pre,r); //若左边近,用左边的左边和右边比次近 } else { bto[i]=s[r].id; ato[i]=opposite(pos,l,s[r].nxt); } if(r) s[r].pre=l; //用完把当前删掉,保证下一个是最西边的 if(l) s[l].nxt=r; } beizeng(); //倍增预处理 X=read(); m=read(); for(int i=1;i<=n;i++) { int tota=0; int totb=0; running(i,tota,totb,X); if(totb&&(((DB)tota/(DB)totb)<minn)) { minn=(DB)tota/(DB)totb; ans=i; } } if(ans==0) ans=s[n].id; put(ans); putchar(' '); for(int i=1;i<=m;i++) { int sta,limit; sta=read(); limit=read(); int tota=0; int totb=0; running(sta,tota,totb,limit); put(tota); putchar(' '); put(totb); putchar(' '); } olinr ~~(0^_^0)+love_nmr; }