用倍增的思想
设 A[ i ] 表示A在 i 位置走一步到达的城市以及经过的路程(这里我用结构体存A[ i ]),B同理
设 f [ i ] [ j ] 表示从 i 位置出发,走 $2^j$ 轮后到达的城市(一轮即AB各走一次)
dis[ i ] [ j ] 表示从 i 位置出发,走 $2^j$ 轮后经过总路程
da [ i ] [ j ] 表示从 i 位置出发,走 $2^j$ 轮后A经过路程,db同理
然后就可以愉快地倍xia增gao了
但是现在有一个问题,怎么预处理出 A[ i ],B[ i ]
把城市编号从大到小加入 set,每次加入一个城市前先求出 set 内离他最近的城市和第二近的城市
如果按高度排序后此高度排名为 i ,那么最近的城市就在 i+1 和 i-1 中,次近的城市就在 i+1,i+2,i-1,i-2 中
这个可以用 lower_bound 求出
然后就可以搞了
思维难度不大,具体实现起来一堆细节恶心得一批
别忘了可能AB一人走一步走不了,但是A单独可以多走一步的情况
关于题目的第一个问题,如果B的路程为0,那么就算A也为0 比值仍为INF
一定仔细看题目啊,很多细节啊
下面附上我那压行严重的巨丑代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<set> using namespace std; typedef long long ll;//有些变量该开long long 果断开 inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1e5+7; const ll INF=5e9+7; int n,m,x0; struct node { ll h; int id;//高度,以及编号 node () { h=0; id=0; } inline bool operator < (const node &tmp) const { return h<tmp.h; } }A[N],B[N],d[N];//在A[i]B[i]中则是距离和编号 multiset <node> s; multiset <node>::iterator it; int f[N][22]; ll dis[N][22],da[N][22],db[N][22]; void pre() { node GG; GG.h=INF; s.insert(GG); s.insert(GG); s.insert(GG); s.insert(GG); GG.h=-INF; s.insert(GG); s.insert(GG); s.insert(GG); s.insert(GG);//防止指针越界 node mx,mxx;/*mx存最近,mxx存次近*/ ll t=0,aa=0,bb=0; for(int i=n;i;i--) { it=s.lower_bound(d[i]); aa=(*it).h; mx.h=aa-d[i].h; mx.id=(*it).id; it--; bb=(*it).h; mxx.h=d[i].h-bb; mxx.id=(*it).id; if(mxx.h<mx.h||(mxx.h==mx.h&&aa>bb)) swap(mx,mxx);//注意如果相等也要判一下 if(!mx.id) mx.h=0;/*如果超出边界了路程统一为0*/ B[i]=mx; it--; t=d[i].h-(*it).h; if(t<mxx.h || (mxx.h==t&&(*it).h<d[mxx.id].h) ) mxx.h=t,mxx.id=(*it).id;//记得相等也要判一下 it++; it++; it++; t=(*it).h-d[i].h; if(t<mxx.h || (mxx.h==t&&(*it).h<d[mxx.id].h) ) mxx.h=t,mxx.id=(*it).id;//记得相等也要判一下 if(!mxx.id) mxx.h=0;/*同样判一下是否超出边界*/ A[i]=mxx; s.insert(d[i]);//别忘了加入d[i] } for(int i=1;i<=n;i++)//预处理倍增数组 { f[i][0]=B[A[i].id].id;//计算f[i][0] if(f[i][0])//如果没出边界就正常处理 { da[i][0]=A[i].h; db[i][0]=B[A[i].id].h; dis[i][0]=da[i][0]+db[i][0]; } else da[i][0]=db[i][0]=dis[i][0]=INF;//不然设成INF } for(int k=1;k<=20;k++) for(int i=1;i<=n;i++) { f[i][k]=f[f[i][k-1]][k-1]; if(f[i][k])//同样要判是否出边界 { da[i][k]=da[i][k-1]+da[f[i][k-1]][k-1]; db[i][k]=db[i][k-1]+db[f[i][k-1]][k-1]; dis[i][k]=dis[i][k-1]+dis[f[i][k-1]][k-1]; } else da[i][k]=db[i][k]=dis[i][k]=INF; } } int ansa,ansb;//存AB的路程 inline void slove(int pos,int x)//给定出发点和最大总路程求AB两人的路程 { ansa=ansb=0;//初始为0 for(int i=20;i>=0;i--)//倍增求路程 { if(x-dis[pos][i]<0) continue; ansa+=da[pos][i]; ansb+=db[pos][i];//更新ansa,ansb x-=dis[pos][i]; pos=f[pos][i];//别忘了更新x和pos } if(x>=A[pos].h) ansa+=A[pos].h;//最后一定要特判A单独多走一步 } void slove_problemA()//处理第一个问题 { x0=read(); int pos=0; double ans=INF,res=0; for(int i=1;i<=n;i++) { slove(i,x0); if(!ansb) res=INF;//判一下B路程为0 else res=(double)ansa/(double)ansb;//ansa和ansb一定要先转double!!因为这个被坑了一天 if( res<ans || (res==ans&&d[i].h>d[pos].h) )//注意如果相等也要判一下 ans=res,pos=i; } printf("%d\n",pos); } void slove_problemB()//处理第二个问题 { m=read(); int s=0,x=0; while(m--) { s=read(); x=read(); slove(s,x); printf("%d %d\n",ansa,ansb); } } int main() { n=read(); for(int i=1;i<=n;i++) d[i].h=read(),d[i].id=i;//d[i].id初始为i pre(); slove_problemA(); slove_problemB(); return 0; }