P1081-开车旅行【倍增,链表,dp】

421 篇文章 4 订阅
29 篇文章 0 订阅

正题

题目大意:https://www.luogu.org/problemnew/show/P1081


题目大意

有若干个城市有不同的海拔 h h h,两个城市之间的距离定义为 ∣ h x − h y ∣ |h_x-h_y| hxhy
小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=fi1,fi1,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=dai1,j,k+dai1,fi1,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=dbi1,j,k+dbi1,fi1,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=fi1,fi1,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=dai1,j,k+dai1,fi1,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=dbi1,j,k+dbi1,fi1,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,0x时就证明可以行走,那就对应修改 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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值