[Luogu] CF515E Drazil and Park

\(Link\)

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\n", 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\n", max(res1, res2));
		}
	}
	return 0;
}
posted @ 2020-11-16 18:44  andysj  阅读(97)  评论(0编辑  收藏  举报