Description
有一只猴子,他生活在一个环形的公园里。有(n)棵树围绕着公园。第(i)棵树和第(i+1)棵树之间的距离是(d_i),而第(n)棵树和第一棵树之间的距离是(d_n)。第(i)棵树的高度是(h_i) 。
这只猴子每天要进行晨跑。晨跑的步骤如下:
- 他先选择两棵树;
- 然后爬上第一棵树;
- 再从第一棵树上下来,接着围绕着公园跑(有两个可能的方向)到第二棵树,然后爬上第二棵树;
- 最后从第二棵树上下来。
但是有一些小孩会在连续的一些树上玩耍。所以猴子不能经过这些树。
比如现在猴子选择的第(x)棵和第(y)棵树,那么该早晨他消耗的能量是(2(h_x+h_y)+dist(x,y)) 。由于某一条路径是被小孩子占据的,所以他只能跑另外一条,因此(dist(x,y))是确定的。
现在给出第(i)天,孩子们会在第(a_i)棵树和(b_i)棵树之间玩耍。具体的,如果(a_i≤b_i) ,那么孩子玩耍的区间就是 ([a_i,b_i]),否则孩子玩耍的区间就是([ai,n]⋃[1,bi]) 。
请帮助这只猴子找出两棵树,让他晨跑的时候他能够消耗最大的能量。
Solution
可能会想到分别最大化,但这时求出来的((x,y))很有可能不是同一对。
所以先断环成链,再对(d_i)做一遍前缀和。这时题目就转化为最大化(2h_x+2h_y+sum_y-sum_x)。套路:把(x)和(y)的项分别处理,这时要使(2h_y+sum_y)最大,(sum_x-2h_x)最小。预处理(ST)表即可。
要注意首先要依据题意,对询问区间求补集,然后算出来的(x),(y)可能是一样的,不符合题意,这时只需在([l,pos-1])和([pos+1,r])中分别查询最值,在计算比较即可。所以(ST)表维护的是最值的下标。
Code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n, m, lg[200005], d[200005], h[200005];
ll s1[200005], s2[200005], sum[200005], mn[200005][20], mx[200005][20];
int read()
{
int x = 0, fl = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
return x * fl;
}
int query1(int x, int y)
{
if (x > y) return 0;
int k = lg[y - x + 1];
ll p = s1[mx[x][k]], q = s1[mx[y - (1 << k) + 1][k]];
if (p > q) return mx[x][k];
else return mx[y - (1 << k) + 1][k];
}
int query2(int x, int y)
{
if (x > y) return 0;
int k = lg[y - x + 1];
ll p = s2[mn[x][k]], q = s2[mn[y - (1 << k) + 1][k]];
if (p < q) return mn[x][k];
else return mn[y - (1 << k) + 1][k];
}
int main()
{
n = read(); m = read();
for (int i = 1; i <= n; i ++ )
{
d[i % n + 1] = read();
d[i % n + n + 1] = d[i % n + 1];
}
for (int i = 1; i <= n; i ++ )
{
h[i] = read();
h[i] <<= 1;
h[i + n] = h[i];
}
for (int i = 1; i <= (n << 1); i ++ )
sum[i] = sum[i - 1] + (ll)(d[i]), lg[i] = (int)(log2((double)(i)));
s1[0] = -2e15; s2[0] = 2e15;
for (int i = 1; i <= (n << 1); i ++ )
{
s1[i] = sum[i] + h[i];
s2[i] = sum[i] - h[i];
mn[i][0] = mx[i][0] = i;
}
for (int j = 1; j <= 19; j ++ )
{
for (int i = 1; i + (1 << j) <= (n << 1); i ++ )
{
ll x, y;
x = s1[mx[i][j - 1]], y = s1[mx[i + (1 << (j - 1))][j - 1]];
if (x > y) mx[i][j] = mx[i][j - 1]; else mx[i][j] = mx[i + (1 << (j - 1))][j - 1];
x = s2[mn[i][j - 1]], y = s2[mn[i + (1 << (j - 1))][j - 1]];
if (x < y) mn[i][j] = mn[i][j - 1]; else mn[i][j] = mn[i + (1 << (j - 1))][j - 1];
}
}
while (m -- )
{
int l = read(), r = read(), pos1, pos2;
if (l <= r) pos1 = query1(r + 1, l + n - 1), pos2 = query2(r + 1, l + n - 1);
else pos1 = query1(r + 1, l - 1), pos2 = query2(r + 1, l - 1);
if (pos1 != pos2) printf("%lld
", s1[pos1] - s2[pos2]);
else
{
int pos3, pos4; ll res1, res2;
if (l <= r)
{
pos3 = query1(r + 1, pos1 - 1), pos4 = query1(pos1 + 1, l + n - 1), res1 = max(s1[pos3], s1[pos4]) - s2[pos2];
pos3 = query2(r + 1, pos2 - 1), pos4 = query2(pos2 + 1, l + n - 1), res2 = s1[pos1] - min(s2[pos3], s2[pos4]);
}
else
{
pos3 = query1(r + 1, pos1 - 1), pos4 = query1(pos1 + 1, l - 1), res1 = max(s1[pos3], s1[pos4]) - s2[pos2];
pos3 = query2(r + 1, pos2 - 1), pos4 = query2(pos2 + 1, l - 1), res2 = s1[pos1] - min(s2[pos3], s2[pos4]);
}
printf("%lld
", max(res1, res2));
}
}
return 0;
}