题解 P1081 【开车旅行】
hicc0305
2018-07-06 20:23:11
主要分为两个部分:预处理和倍增求解
## 预处理
主要的难题是预处理出当前城市的最近和次近城市,n^2的暴力枚举显然会超时。下面是巧妙的正解:
先按照城市的高度进行排序,不管位置。排序后用双向链表链接各个城市。再按照原来的顺序,也就是按照原来城市的位置顺序进行预处理。排序后,显然,最近和次近一定在i-2,i-1,i+1,i+2中。得出了最近和次近后,就把当前城市删掉,这样后面扫到的所有城市,都不可能出现其他城市在当前城市西边的情况。
## 倍增
用fa[i][j]表示a从j出发走2^i天行驶的路程,fb[i][j]表示b从j出发走2^i天行驶的路程。然后,一直逼近就行了
```cpp
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,l,r,j;
struct node
{
int i, v, l, r;
}d[100005];
int h[100005], p[100005];
int fa[100005][21], fb[100005][21], f[100005][21];
int na[100005],nb[100005],a,b,ans=n;
double minn=1000000000;
bool cmp(node x,node y)
{
return x.v<y.v;
}
bool dir()
{
if(!l) return 0;
if(!r) return 1;
return d[j].v-d[l].v<=d[r].v-d[j].v;
}
int pd(int a, int b)
{
if(!a) return d[b].i;
if(!b) return d[a].i;
if(d[j].v-d[a].v<=d[b].v-d[j].v) return d[a].i;
return d[b].i;
}
void st()
{
for(int j=1;j<=20;j++)
{
for(int i=1;i<=n;i++)
{
f[i][j]=f[f[i][j-1]][j-1];
fa[i][j]=fa[i][j-1]+fa[f[i][j-1]][j-1];
fb[i][j]=fb[i][j-1]+fb[f[i][j-1]][j-1];
}
}
}
void get(long long x, int p)
{
a=b=0;
for(int i=20;i>=0;i--)
{
if(f[p][i] && (long long)(a+b+fa[p][i]+fb[p][i])<=x)
{
a+=fa[p][i];
b+=fb[p][i];
p=f[p][i];
}
}
if(na[p] && a+b+fa[p][0]<=x) a+=fa[p][0];
}
int main()
{
long long x;
scanf("%d", &n);
for(int i=1;i<=n;i++) scanf("%d",&d[i].v);
for(int i=1;i<=n;i++) d[i].i=i;
sort(d+1,d+n+1,cmp);
for(int i=1;i<=n;i++) p[d[i].i]=i;
for(int i=1;i<=n;i++) d[i].l=i-1,d[i].r=i+1;
d[1].l=d[n].r=0;
for(int i=1;i<=n;i++)
{
j=p[i];l=d[j].l;r=d[j].r;
if(dir()) nb[i]=d[l].i,na[i]=pd(d[l].l,r);
else nb[i]=d[r].i,na[i]=pd(l,d[r].r);
if(l) d[l].r=r;
if(r) d[r].l=l;
}
for(int i=1;i<=n;i++)
{
f[i][0]=nb[na[i]];
fa[i][0]=abs(d[p[i]].v-d[p[na[i]]].v);
fb[i][0]=abs(d[p[f[i][0]]].v-d[p[na[i]]].v);
}
st();
scanf("%lld%d",&x,&m);
for(int i=1;i<=n;i++)
{
get(x,i);
if(b && ((double)a)/((double)b)<minn)
{
minn=((double)a)/((double)b);
ans=i;
}
}
printf("%d\n",ans);
for(int i=1;i<=m;i++)
{
scanf("%d%lld",&j,&x);
get(x,j);
printf("%d %d\n",a,b);
}
return 0;
}
```