【开车旅行】
这是一道非常可怕的题
细节非常之多,就连\(INF\)设置的太小都会导致离奇错误
根据这道题一堆废话之后,我们首先要处理的是对于每个点,他下面那个要到达的点是谁
也就是距离他最近的点和次近的点分别是谁
看起来好像有些鬼,但是我们想一想这个距离是怎么定义的
“城市 \(i\) 和城市 $ j$之间的距离 \(d[i,j]\) 恰好是这两个城市海拔高度之差的绝对值,即$ d[i,j]=|h[i]-h[j]|$ 。”
最近的那个点显然就是前驱或者后继啊
那我们要是有一棵平衡树该多好啊
但是对于这种平衡树我们完全不用去手写,我们用\(set\)来代替就可以了
我们只需要\(s.find\)一下,之后左移右移一下迭代器就好了
之后对于次近的点
-
如果最近点是前驱,那么这个点肯定是后继或者是前驱的前驱
-
如果最近点是后继,那么这个点肯定是前驱或者是后继的后继
这里涉及到迭代器移动好几位,有可能越界
所以提前在\(set\)里插入一些\(INF\)和\(-INF\)
这里的\(INF\)一定要足够大,否则就wa了
之后呢,我们看到对于每个点我们都需要往前开车,我们也可以暴力枚举一步一步的跳,但是这样的复杂度是\(O(n^2)\)的
那有没有什么高效的跳的方法呢
那就是倍增
由于这里跳两次\(2^{j-1}\)步可以合并成跳\(2^j\)步,于是我们可以倍增
我们设\(f[i][j][opt]\)表示从\(i\)这个点由\(opt\)(0或1)跳\(2^j\)步能到达的点是谁
其中1代表A先走,0代表B先走
于是我们还需要一个数组来记录这样跳一共走了多少的距离
于是又有\(dp[i][j][opt]\)表示从\(i\)这个点由\(opt\)(0或1)跳\(2^j\)步走的距离是多少
我们还需要统计答案啊
我们需要知道A和B分别走了多少
于是又有\(d[i][j]\)表示从\(i\)这个点由A跳\(2^j\)步A走的距离是多少
至于这几个倍增数组怎么预处理
那写过树上倍增LCA的人应该都会的
至于怎么跳,其实就跟树上的倍增一样,我们从大里往小枚举这一步跳还是不跳就行了
于是代码
#include<set>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstdlib>
#define re register
#define mp make_pair
#define maxn 100001
#define LL long long
#define INF 990854775807
using namespace std;
typedef pair<int,int> pii;
multiset<pii> s;
int h[maxn],ans;
int t1[maxn],t2[maxn];
int ss[maxn],xx[maxn];
int f[maxn][18][2];
LL ansa[maxn],ansb[maxn];
LL dp[maxn][18][2],d[maxn][18];
int x0;
int n,m,H;
pii mid1,mid2,mid3;
multiset<pii>::iterator it;
LL aa=INF,bb=1;
inline int read()
{
char c=getchar();
int x=0,r=1;
while(c<'0'||c>'9')
{
if(c=='-') r=-1;
c=getchar();
}
while(c>='0'&&c<='9')
x=(x<<3)+(x<<1)+c-48,c=getchar();
return x*r;
}
inline void jump(int sq,LL now,int t)
{
ansa[t]=0;
ansb[t]=0;
for(re int i=H;i>=0;i--)
if(f[sq][i][1]&&now-dp[sq][i][1]>=0)
{
now-=dp[sq][i][1];
ansa[t]+=d[sq][i];
ansb[t]+=dp[sq][i][1]-d[sq][i];
sq=f[sq][i][1];
}
}
inline LL gcd(LL a,LL b)
{
if(!b) return a;
return gcd(b,a%b);
}
inline void check(int x)
{
if(!ansb[0]) ansa[0]=INF,ansb[0]=1;
if(ansa[0]==INF)
{
if(aa/bb==INF&&h[ans]<h[x]) ans=x,aa=1,bb=1;
return;
}
LL r=gcd(ansa[0],ansb[0]);
ansa[0]/=r,ansb[0]/=r;
if(ansb[0]==bb&&ansa[0]==aa)
{
if(h[ans]<h[x]) ans=x;
return;
}
if(double(ansa[0])/double(ansb[0])<double(aa)/double(bb)) ans=x,aa=ansa[0],bb=ansb[0];
}
int main()
{
n=read();
H=log2(n);
for(re int i=1;i<=n;i++)
h[i]=read();
t1[n-1]=n;
dp[n-1][0][0]=abs(h[n]-h[n-1]);
s.insert(mp(h[n],n));
s.insert(mp(h[n-1],n-1));
s.insert(mp(INF,0));
s.insert(mp(-INF,0));
s.insert(mp(INF,0));
s.insert(mp(-INF,0));
for(re int i=n-2;i;i--)
{
s.insert(mp(h[i],i));
it=s.find(mp(h[i],i));
it--;it--;
if(s.begin()==it)
{
it++,it++,it++;
mid1=*it;
t1[i]=mid1.second;
dp[i][0][0]=mid1.first-h[i];
it++;
mid2=*it;
t2[i]=mid2.second;
dp[i][0][1]=d[i][0]=mid2.first-h[i];
}
else
{
it++,it++;
it++;
mid1=*it;
it--;it--;
mid2=*it;
if(h[i]-mid2.first<=mid1.first-h[i]) t1[i]=mid2.second;
else t1[i]=mid1.second;
if(t1[i]==mid2.second)
{
it--;
mid2=*it;
}else it++,it++,it++,mid1=*it;
LL a1=abs(mid1.first-h[i]),a2=abs(mid2.first-h[i]);
if(a2<=a1) t2[i]=mid2.second;
else t2[i]=mid1.second;
dp[i][0][0]=abs(h[t1[i]]-h[i]);
dp[i][0][1]=d[i][0]=abs(h[t2[i]]-h[i]);
}
}
x0=read();
m=read();
for(re int i=1;i<=m;i++)
ss[i]=read(),xx[i]=read();
for(re int i=1;i<=n;i++)
f[i][0][0]=t1[i],f[i][0][1]=t2[i];
for(re int i=1;i<=n;i++)
for(re int opt=0;opt<=1;opt++)
{
f[i][1][opt]=f[f[i][0][opt]][0][opt^1];
dp[i][1][opt]=dp[i][0][opt]+dp[f[i][0][opt]][0][opt^1];
if(opt) d[i][1]=d[i][0];
}
for(re int i=2;i<=H;i++)
for(re int j=1;j<=n;j++)
for(re int opt=0;opt<=1;opt++)
{
f[j][i][opt]=f[f[j][i-1][opt]][i-1][opt];
dp[j][i][opt]=dp[j][i-1][opt]+dp[f[j][i-1][opt]][i-1][opt];
if(opt) d[j][i]=d[j][i-1]+d[f[j][i-1][1]][i-1];
}
ans=1;
jump(1,x0,0);
aa=ansa[0];
bb=ansb[0];
if(!bb) aa=INF,bb=1;
LL r=gcd(aa,bb);
aa/=r;bb/=r;
for(re int i=2;i<=n;i++)
jump(i,x0,0),check(i);
for(re int i=1;i<=m;i++)
jump(ss[i],xx[i],i);
cout<<ans<<endl;
for(re int i=1;i<=m;i++)
printf("%lld %lld",ansa[i],ansb[i]),putchar(10);
return 0;
}