【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;
}