【Luogu P1081】[NOIP2012 提高组] 开车旅行

链接:

洛谷

题目大意:

小 A、小 B 从左往右走,小 A 会走差值次小高度,小 B 走差值最小高度。

给定总路程限制,求出两人路程最小比。

给定起点和限制,求两人路程。

正文:

在做题时会冒出一个想法:可能要先预处理出所有位置的下一个位置。很明显,这是解决本题的“先行官”。

我们可以通过链表,对它排序后,把前后指针指向 \(i+1,i-1\)。然后判断大小得到小 A、小 B 的下一位置,每操作一次,就将当前节点删掉,以免后面节点误选此节点。

我们求出了两人的下一位置 \(\text{Ato}_i,\text{Bto}_i\)

\(\text{disA}_{i,j,k},\text{disB}_{i,j,k}\quad(k\in[0,1])\) 分别表示在 \(i\) 节点处小 A/小 B 跑了 \(2^j\) 步时小 A 的总距离和在 \(i\) 节点处小 A/小 B 跑了 \(2^j\) 步时小 B 的总距离。

还需设 \(f_{i,j,k}\) 表示在 \(i\) 节点处小 A/小 B 跑了 \(2^j\) 步时的位置。

显然有:

\[\begin{aligned} f_{i,j,k} &= \left\{\begin{matrix} \text{Ato}_i & (j=0,k=0)\\ \text{Bto}_i & (j=0,k=1)\\ f_{f_{i,j-1,k},j-1,k\oplus1} & (i=1)\\ f_{f_{i,j-1,k},j-1,k} & (i>1) \end{matrix}\right. \\ \text{disA}_{i,j,k} &= \left\{\begin{matrix} |h_i-h_{\text{Ato}_i}| & (j=0,k=0)\\ 0 & (j=0,k=1)\\ \text{disA}_{i,j-1,k}+\text{disA}_{f_{i,j-1,k},j-1,k\oplus1} & (i=1)\\ \text{disA}_{i,j-1,k}+\text{disA}_{f_{i,j-1,k},j-1,k} & (i>1)\\ \end{matrix}\right. \\ \text{disB}_{i,j,k} &= \left\{\begin{matrix} 0 & (j=0,k=0)\\ |h_i-h_{\text{Bto}_i}| & (j=0,k=1)\\ \text{disB}_{i,j-1,k}+\text{disB}_{f_{i,j-1,k},j-1,k\oplus1} & (i=1)\\ \text{disB}_{i,j-1,k}+\text{disB}_{f_{i,j-1,k},j-1,k} & (i>1)\\ \end{matrix}\right. \end{aligned}\]

求完后倍增跑从 \(s\) 出发的两人路程即可。

代码:


const int N = 1e5 + 5;

inline ll Read()
{
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

int n, m;
int pos[N], Ato[N], Bto[N];
struct node
{
	int id, pre, nxt;
	ll val;
	bool operator < (const node &a) const
	{
		return val < a.val;
	}
	node() {}
	node (int i, ll v) {id = i, val = v;} 
}h[N];

int Choose(int i, int a, int b)
{
	if (!a) return h[b].id;
	if (!b) return h[a].id;
	if (h[i].val - h[a].val <= h[b].val - h[i].val)
		return h[a].id;
	else return h[b].id;
}

void Delete(int p)
{
	if (h[p].pre) h[h[p].pre].nxt = h[p].nxt; 
	if (h[p].nxt) h[h[p].nxt].pre = h[p].pre;
}

void Prework()
{
	sort (h + 1, h + 1 + n);
	for (int i = 1; i <= n; i++)
	{
		pos[h[i].id] = i;
		h[i].pre = i - 1;
		h[i].nxt = i + 1;
	}
	h[1].pre = h[n].nxt = 0;
	for (int i = 1; i < n; i++)
	{
		int p = pos[i], p1 = h[p].pre, p2 = h[p].nxt;
		if (p1 && (h[p].val - h[p1].val <= h[p2].val - h[p].val || !p2))
			Bto[i] = h[p1].id, Ato[i] = Choose(p, h[p1].pre, p2);
		else
			Bto[i] = h[p2].id, Ato[i] = Choose(p, p1, h[p2].nxt);
		Delete(p);
	}
} 

ll disA[N][30][2], disB[N][30][2], tmpA, tmpB, Log;
int f[N][30][2];

void Solve(int s, ll x)
{
	tmpA = tmpB = 0; int k = 0;
	for (int j = Log; ~j; j--)
		if (f[s][j][k] && disA[s][j][k] + disB[s][j][k] <= x)
		{
			x -= disA[s][j][k] + disB[s][j][k];
			tmpA += disA[s][j][k], tmpB += disB[s][j][k];
			if(j == 0) k ^= 1;
			s = f[s][j][k];
		}
}

int main()
{
	n = Read();
	for (int i = 1; i <= n; i++)
		h[i] = node(i, Read());
	
	Prework();
	
	for (int i = 1; i <= n; i++)
	{
		if (Ato[i])
			f[i][0][0] = Ato[i],
			disA[i][0][0] = abs(h[pos[i]].val - h[pos[Ato[i]]].val);
		if (Bto[i])
			f[i][0][1] = Bto[i],
			disB[i][0][1] = abs(h[pos[i]].val - h[pos[Bto[i]]].val);
	}
	Log = log2(n) + 1;
	for (int j = 1; j <= Log; j++)
		for (int i = 1; i <= n; i++)
			for (int k = 0; k <= 1; k++)
			{
				if(f[i][j - 1][k]) f[i][j][k] = f[f[i][j - 1][k]][j - 1][j == 1? 1 ^ k: k];
				if(f[i][j][k]) disA[i][j][k] = disA[i][j - 1][k] + disA[f[i][j - 1][k]][j - 1][j == 1? 1 ^ k: k],
				               disB[i][j][k] = disB[i][j - 1][k] + disB[f[i][j - 1][k]][j - 1][j == 1? 1 ^ k: k];
			}
	ll s, x = Read(), ansA = 1, ansB = 0, id = 0;
	for (int i = 1; i <= n; i++)
	{
		Solve(i, x);
		if (!tmpB) tmpA = 1;
		if (tmpA * ansB < tmpB * ansA || (tmpA * ansB == tmpB * ansB && h[pos[i]].val > h[pos[id]].val))
			ansA = tmpA, ansB = tmpB, id = i;
	}
	printf ("%lld\n", id);
	for (m = Read(); m--; )
	{
		s = Read(), x = Read();
		Solve(s, x);
		printf ("%lld %lld\n", tmpA, tmpB);
	}
	return 0;
}
posted @ 2021-06-13 11:38  Jayun  阅读(80)  评论(0编辑  收藏  举报