正题
题目大意:https://www.luogu.org/problemnew/show/P1081
题目大意
有若干个城市有不同的海拔
h
h
h,两个城市之间的距离定义为
∣
h
x
−
h
y
∣
|h_x-h_y|
∣hx−hy∣
小A每次走次近的,小B每次走最近的。它们轮流开车。且只会往编号更大的城市开。
问一:在距离
≤
X
\leq X
≤X的情况下求一个起点是的他们两个开的距离之和最小。
问二:若干个
S
i
,
X
i
S_i,X_i
Si,Xi求在
S
i
S_i
Si出发,距离
≤
X
i
\leq X_i
≤Xi时他们各开多远。
解题思路
首先链表(或平衡树)预处理出 g a i ga_i gai和 g b i gb_i gbi表示在第 i i i个城市小 A A A或小 B B B下一个会到的城市。
然后考虑
d
p
dp
dp
设
f
i
,
j
,
0
/
1
f_{i,j,0/1}
fi,j,0/1表示从
j
j
j出发走了
2
i
2^i
2i次,到小A/小B开车,到达的地点。
设
d
a
i
,
j
,
0
/
1
da_{i,j,0/1}
dai,j,0/1表示从
j
j
j出发走了
2
i
2^i
2i次,到小A/小B开车,小
A
A
A走了多远。
设
d
b
i
,
j
,
0
/
1
db_{i,j,0/1}
dbi,j,0/1表示从
j
j
j出发走了
2
i
2^i
2i次,到小A/小B开车,小
B
B
B走了多远。
然后首先
f
0
,
j
,
0
=
g
a
j
,
f
0
,
j
,
1
=
g
b
j
f_{0,j,0}=ga_j,f_{0,j,1}=gb_j
f0,j,0=gaj,f0,j,1=gbj
d
a
0
,
j
,
0
=
d
i
s
t
(
j
,
g
a
j
)
,
d
a
0
,
j
,
1
=
0
da_{0,j,0}=dist(j,ga_{j}),da_{0,j,1}=0
da0,j,0=dist(j,gaj),da0,j,1=0
d
b
0
,
j
,
1
=
d
i
s
t
(
j
,
g
b
j
)
,
d
b
0
,
j
,
0
=
0
db_{0,j,1}=dist(j,gb_{j}),db_{0,j,0}=0
db0,j,1=dist(j,gbj),db0,j,0=0
然后考虑转移
当
i
=
1
i=1
i=1时
f
i
,
j
,
k
=
f
i
−
1
,
f
i
−
1
,
j
,
k
,
k
x
o
r
1
f_{i,j,k}=f_{i-1,f_{i-1,j,k},k\ xor\ 1}
fi,j,k=fi−1,fi−1,j,k,k xor 1
d
a
i
,
j
,
k
=
d
a
i
−
1
,
j
,
k
+
d
a
i
−
1
,
f
i
−
1
,
j
,
k
,
k
x
o
r
1
da_{i,j,k}=da_{i-1,j,k}+da_{i-1,f_{i-1,j,k},k\ xor\ 1}
dai,j,k=dai−1,j,k+dai−1,fi−1,j,k,k xor 1
d
b
i
,
j
,
k
=
d
b
i
−
1
,
j
,
k
+
d
b
i
−
1
,
f
i
−
1
,
j
,
k
,
k
x
o
r
1
db_{i,j,k}=db_{i-1,j,k}+db_{i-1,f_{i-1,j,k},k\ xor\ 1}
dbi,j,k=dbi−1,j,k+dbi−1,fi−1,j,k,k xor 1
但是因为
i
>
1
i>1
i>1时
2
i
2^i
2i依旧是偶数,说以
k
k
k不需要改变
f
i
,
j
,
k
=
f
i
−
1
,
f
i
−
1
,
j
,
k
,
k
f_{i,j,k}=f_{i-1,f_{i-1,j,k},k}
fi,j,k=fi−1,fi−1,j,k,k
d
a
i
,
j
,
k
=
d
a
i
−
1
,
j
,
k
+
d
a
i
−
1
,
f
i
−
1
,
j
,
k
,
k
da_{i,j,k}=da_{i-1,j,k}+da_{i-1,f_{i-1,j,k},k}
dai,j,k=dai−1,j,k+dai−1,fi−1,j,k,k
d
b
i
,
j
,
k
=
d
b
i
−
1
,
j
,
k
+
d
b
i
−
1
,
f
i
−
1
,
j
,
k
,
k
db_{i,j,k}=db_{i-1,j,k}+db_{i-1,f_{i-1,j,k},k}
dbi,j,k=dbi−1,j,k+dbi−1,fi−1,j,k,k
处理好之后,我们定义函数
c
a
l
c
(
s
,
x
)
calc(s,x)
calc(s,x)表示
从
s
s
s出发,且距离不超过
x
x
x时小
A
A
A和小
B
B
B的行驶距离。
首先我们要设定 n o w , l a , l b now,la,lb now,la,lb表示现在在哪个点和小 A A A和小 B B B的行驶距离。
然后当 l a + l b + d a i , n o w , 0 + d b i , n o w , 0 ≤ x la+lb+da_{i,now,0}+db_{i,now,0}\leq x la+lb+dai,now,0+dbi,now,0≤x时就证明可以行走,那就对应修改 n o w , l a , l b now,la,lb now,la,lb的值。
c o d e code code
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#define ll long long
using namespace std;
const ll N=101000;
struct Node{
ll h,id,prev,next;
}l[N];
set<int> BST;
double ans;
ll n,m,mark,la,lb,t;
ll h[N],f[21][N][2],da[21][N][2];
ll ga[N],gb[N],db[21][N][2],pre[N];
bool left(ll x){
if(!l[x].prev) return 0;
if(!l[x].next) return 1;
return l[x].h-l[l[x].prev].h<=l[l[x].next].h-l[x].h;
}
ll cmps(ll a,ll b,ll x){
if(!a) return l[b].id;
if(!b) return l[a].id;
if(l[x].h-l[a].h<=l[b].h-l[x].h) return l[a].id;
return l[b].id;
}
bool cmp(Node x,Node y)
{return x.h<y.h;}
void Part1()
{
for(ll i=1;i<=n;i++)
l[i].h=h[i],l[i].id=i;
sort(l+1,l+1+n,cmp);
for(ll i=1;i<=n;i++)
pre[l[i].id]=i;
for(ll i=1;i<=n;i++)
l[i].prev=i-1,l[i].next=i+1;
l[1].prev=l[n].next=0;
for(ll i=1;i<=n;i++)
{
ll j=pre[i];
if(left(j))
gb[i]=l[l[j].prev].id,
ga[i]=cmps(l[l[j].prev].prev,l[j].next,j);
else
gb[i]=l[l[j].next].id,
ga[i]=cmps(l[j].prev,l[l[j].next].next,j);
l[l[j].prev].next=l[j].next;
l[l[j].next].prev=l[j].prev;
}
}
ll dist(ll x,ll y)
{return abs(h[x]-h[y]);}
void Part2()
{
for(ll j=1;j<=n;j++)
{
if(ga[j]){
f[0][j][0]=ga[j];
da[0][j][0]=dist(j,ga[j]);
db[0][j][0]=0;
}
if(gb[j]){
f[0][j][1]=gb[j];
da[0][j][1]=0;
db[0][j][1]=dist(j,gb[j]);
}
}
ll L=0;
for(ll i=1;i<=t;i++)
for(ll j=1;j<=n;j++)
for(ll k=0;k<2;k++)
{
if(i==1) L=k^1;
else L=k;
f[i][j][k]=f[i-1][f[i-1][j][k]][L];
da[i][j][k]=da[i-1][j][k]+da[i-1][f[i-1][j][k]][L];
db[i][j][k]=db[i-1][j][k]+db[i-1][f[i-1][j][k]][L];
}
}
void calc(ll s,ll x)
{
ll now=s;
la=lb=0;
for(ll i=t;i>=0;i--)
{
if(f[i][now][0]&&la+lb+da[i][now][0]+db[i][now][0]<=x){
la+=da[i][now][0];
lb+=db[i][now][0];
now=f[i][now][0];
if(!now) break;
}
}
}
int main()
{
scanf("%lld",&n);
t=(int)(log(n*1.0)/log(2.0)+0.001);
for(ll i=1;i<=n;i++)
scanf("%lld",&h[i]);
Part1();
Part2();
ll X;
scanf("%lld",&X);
ans=2147483647;
for(ll i=1;i<=n;i++)
{
calc(i,X);
if(lb&&((double)la/lb)<ans)
ans=(double)la/lb,mark=i;
}
printf("%lld\n",mark);
scanf("%lld",&m);
for(ll i=1;i<=m;i++)
{
ll s;
scanf("%lld%lld",&s,&X);
calc(s,X);
printf("%lld %lld\n",la,lb);
}
}