开车旅行【DP】【倍增】【双向链表】

>Link

luogu P1081

ybtoj开车旅行


>Description

在这里插入图片描述


>解题思路

先预处理 n n n个点,从每一个点出发,最近的下个点和第二近的下个点
因为距离为两点的海拔高度差,那我们就可以先按照海拔高度排个序,那最近的点就在它的前驱和后驱之中,第二近的点就在前驱、后驱、前驱的前驱、后驱的后驱之中
那我们从 1 1 1 n n n处理完一个点后,要把当前点删去,因为题目规定了后面的点不能到达前面的点
这个可以用双向链表维护,也可以用平衡树
(要注意是否越界QwQ)

对于第二个问题,由于必须先有一个 O ( n ) O(n) O(n),所以我们往 O ( n l o g n ) O(nlogn) O(nlogn)考虑

设两个数组倍增DP:
f i , j , 0 / 1 f_{i,j,0/1} fi,j,0/1:从第 i i i个点出发,走 2 j 2^j 2j步,小A / 小B 先走最后到达的终点
d i s i , j , 0 / 1 dis_{i,j,0/1} disi,j,0/1:从第 i i i个点出发,走 2 j 2^j 2j步,小A先走,小A / 小B走过的路程
然后我们DP就行了(转移方程还挺好推得,就设置状态不会QAQ)

然后我们按照题意处理就行了,最多走 x x x路程,那我们就倍增跳着走,在 x x x的限制下尽量走多的路程


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 100010
#define inf 20000000000
#define LL long long
#define db long double
using namespace std;

struct node
{
    
    
	int id, l, r;
	LL h;
} a[N];
struct SUM
{
    
    
	LL s1, s2;
};
int n, pos[N], to[N][5], f[N][30][5], dis[N][30][5], g;
int m, s, ans1;
LL w[N][5], H[N], x;
db ans;

bool cmp (node aa, node bb) {
    
    return aa.h < bb.h;}
SUM solve (int s, int x) //从s出发,最多走x路程
{
    
    
	LL s1 = 0, s2 = 0;
	for (int j = 20; j >= 0; j--)
	  if (f[s][j][0] && s1 + s2 + dis[s][j][0] + dis[s][j][1] <= x)
	  {
    
    
	  	s1 += dis[s][j][0];
		s2 += dis[s][j][1];
		s = f[s][j][0];
	  }
	return (SUM){
    
    s1, s2};
}

int main()
{
    
    
	scanf ("%d", &n);
	for (int i = 1; i <= n; i++)
	{
    
    
		scanf ("%lld", &H[i]);
		a[i].h = H[i], a[i].id = i;
		if (H[i] > H[ans1]) ans1 = i;
	}
	sort (a + 1, a + 1 + n, cmp);
	for (int i = 1; i <= n; i++)
	{
    
    
		a[i].l = i - 1, a[i].r = i + 1;
		pos[a[i].id] = i;
	}
	int m1, m2, p, l, r; LL mn1, mn2;
	for (int i = 1; i <= n; i++)
	{
    
    
		m1 = m2 = 0, mn1 = mn2 = inf;
		p = pos[i];
		l = a[p].l, r = a[p].r;
		if (a[r].h - a[p].h <= mn1 && r && r <= n)
		    m1 = r, mn1 = a[r].h - a[p].h;
		if (a[p].h - a[l].h <= mn1 && l && l <= n)
		    m1 = l, mn1 = a[p].h - a[l].h;
		
		if (a[a[r].r].h - a[p].h <= mn2 && a[r].r && a[r].r <= n)
		  m2 = a[r].r, mn2 = a[a[r].r].h - a[p].h;
		if (a[r].h - a[p].h <= mn2 && r != m1 && r && r <= n)
		  m2 = r, mn2 = a[r].h - a[p].h;
		if (a[p].h - a[l].h <= mn2 && l != m1 && l && l <= n)
		  m2 = l, mn2 = a[p].h - a[l].h;
		if (a[p].h - a[a[l].l].h <= mn2 && a[l].l && a[l].l <= n)
		  m2 = a[l].l, mn2 = a[p].h - a[a[l].l].h;
		
		to[i][0] = a[m1].id, to[i][1] = a[m2].id;
		w[i][0] = mn1, w[i][1] = mn2;
		a[a[p].l].r = a[p].r, a[a[p].r].l = a[p].l;
		
	//	printf ("%d %lld  %d %lld\n", to[i][0], w[i][0], to[i][1], w[i][1]);
	}
	// A->2nd  B->1st
	for (int i = n; i >= 1; i--)
	{
    
    
		f[i][0][0] = to[i][1];
		f[i][0][1] = to[i][0];
		f[i][1][0] = to[f[i][0][0]][0];
		f[i][1][1] = to[f[i][0][1]][1];
		dis[i][0][0] = dis[i][1][0] = w[i][1];
		dis[i][0][1] = 0;
		dis[i][1][1] = w[f[i][0][0]][0];
		for (int j = 2; j <= 20; j++)
		{
    
    
			f[i][j][0] = f[f[i][j - 1][0]][j - 1][0];
			dis[i][j][0] = dis[i][j - 1][0] + dis[f[i][j - 1][0]][j - 1][0];
			f[i][j][1] = f[f[i][j - 1][1]][j - 1][1];
			dis[i][j][1] = dis[i][j - 1][1] + dis[f[i][j - 1][0]][j - 1][1];
		}
	}
	scanf ("%lld", &x);
	ans = inf, ans1 = 0;
	LL sum1, sum2;
	SUM g;
	for (int i = 1; i <= n; i++)
	{
    
    
		g = solve (i, x);
		sum1 = g.s1, sum2 = g.s2;
		if (sum1 == sum2 && sum1)
		{
    
    
			if (ans > (db)1) ans = (db)1, ans1 = i;
			else if (ans == (db)1 && H[ans1] < H[i]) ans1 = i;
		}
		else if (sum2)
		{
    
    
			if (ans > (db)sum1 / (db)sum2)
			  ans = (db)sum1 / (db)sum2, ans1 = i;
			else if (ans == (db)sum1 / (db)sum2 && H[ans1] < H[i])
			  ans1 = i;
		}
	}
	printf ("%d\n", ans1);
	scanf ("%d", &m);
	while (m--)
	{
    
    
		scanf ("%d%lld", &s, &x);
		g = solve (s, x);
		printf ("%lld %lld\n", g.s1, g.s2);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43010386/article/details/120958643