https://www.luogu.org/problemnew/show/P1081
首先 50 50 分的暴力跟着晦涩的题意强行打了一遍,然后对题意有了更深刻的理解,发现可以预处理小 A A 和小 B B 从某个点出发下一个走的点 (n2) ( n 2 ) ,就不用每次 for f o r 一遍找了,这样就成了 70 70 分。
不管是第一近还是第二近的点,都是因为海拔相近,所以用链表或set维护一个海拔有序的序列,然后按一定顺序删除/加入,在 −2,−1,+1,+2 − 2 , − 1 , + 1 , + 2 的位置查找即可。
知道了走一步的位置,那么走 2k 2 k 步也能应声而出了,预处理倍增数组,然后倒置循环倍增跳跃即可。
#include
#define FOR(i,x,y) for(register int i=(x);i<=(y);++i)
#define DOR(i,x,y) for(register int i=(x);i>=(y);--i)
#define N 100003
typedef long long LL;
using namespace std;
int h[N],L[N],R[N],idx[N],nxt[N][2],dis[N][2],g[N][23];
LL f[N][23][2];
int n,m;
struct node
{
int x,val;
bool operator <(const node &_)const{return val<_.val;}
}a[N];
void init()
{
memset(dis,0x7f,sizeof(dis));
FOR(i,1,n)a[i]=(node){i,h[i]};
sort(a+1,a+1+n);
FOR(i,1,n)idx[a[i].x]=i;
FOR(i,1,n)L[i]=i-1,R[i]=i+1;
L[1]=R[n]=0;
FOR(i,1,n)
{
int k=idx[i];
if(L[k])
{
k=L[k];
nxt[i][0]=a[k].x,dis[i][0]=h[i]-a[k].val;
if(L[k])
{
k=L[k];
nxt[i][1]=a[k].x,dis[i][1]=h[i]-a[k].val;
}
}
k=idx[i];
if(R[k])
{
k=R[k];
if(a[k].val-h[i]0])
{
nxt[i][1]=nxt[i][0],dis[i][1]=dis[i][0];
nxt[i][0]=a[k].x,dis[i][0]=a[k].val-h[i];
}
else if(a[k].val-h[i]1])
nxt[i][1]=a[k].x,dis[i][1]=a[k].val-h[i];
if(R[k])
{
k=R[k];
if(a[k].val-h[i]1])
nxt[i][1]=a[k].x,dis[i][1]=a[k].val-h[i];
}
}
k=idx[i];
L[R[k]]=L[k],R[L[k]]=R[k];
}
FOR(i,1,n)
{
g[i][0]=nxt[nxt[i][1]][0];
f[i][0][1]=dis[i][1];
f[i][0][0]=dis[nxt[i][1]][0];
}
FOR(j,1,20)
FOR(i,1,n)
{
g[i][j]=g[g[i][j-1]][j-1];
f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];
f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];
}
}
void Travel(int s,int X,LL &A,LL &B)
{
A=B=0;
DOR(i,20,0)
if(g[s][i]&&A+B+f[s][i][1]+f[s][i][0]<=X)
{
A+=f[s][i][1];
B+=f[s][i][0];
s=g[s][i];
}
if(nxt[s][1]&&A+B+dis[s][1]<=X)
A+=dis[s][1];
}
bool Smaller(LL a,LL b,LL c,LL d)
{
if(b==0)return false;
else if(d==0)return true;
else return 1.0*a/b<1.0*c/d;
}
bool Equal(LL a,LL b,LL c,LL d)
{
if(b==0&&d==0)return 1;
else if(b*d==0)return 0;
else return fabs(1.0*a/b-1.0*c/d)<=1e-7;
}
int main()
{
scanf("%d",&n);
FOR(i,1,n)scanf("%d",&h[i]);
init();
int X0,ansH=-2e9,ansS;
LL ansA=2e9,ansB=0;
scanf("%d",&X0);
FOR(i,1,n)
{
LL resA,resB;
Travel(i,X0,resA,resB);
if(Smaller(resA,resB,ansA,ansB)||Equal(resA,resB,ansA,ansB)&&h[i]>ansH)
{
ansS=i;
ansH=h[i];
ansA=resA,ansB=resB;
}
}
printf("%d\n",ansS);
scanf("%d",&m);
while(m--)
{
int s,t;LL A,B;
scanf("%d%d",&s,&t);
Travel(s,t,A,B);
printf("%lld %lld\n",A,B);
}
return 0;
}