线段树——51nod1593&CF515E 公园晨跑

Description

luogu传送门
51nod传送门

有一只猴子,他生活在一个环形的公园里。有\(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\leq b_i\),那么孩子玩耍的区间就是\([a_i,b_i]\),否则孩子玩耍的区间就是\([a_i,n]⋃[1,b_i]\)

请帮助这只猴子找出两棵树,让他晨跑的时候他能够消耗最大的能量。

输入
单组测试数据。
第一行有两个整数\(n\)\(m\)\((3\leq n\leq 10^5,1\leq m\leq 10^5)\),表示树的数目,以及猴子跑步的天数。
第二行有\(n\)个整数\(d_1,d_2,...,d_n (1\leq d_i\leq 10^9)\),表示树之间的距离。
第三行有\(n\)个整数\(h_1,h_2,...,h_n (1\leq h_i\leq 10^9)\),表示树的高度。
接下来\(m\)行,第一行有两个整数 \(a_i\)\(b_i\)\((1\leq a_i,b_i\leq n)\),描述每一天孩子玩耍的区间。输入保证至少有两个棵树孩子不会进行玩耍,这样猴子每天都可以晨跑了。
输出
对于每一天,输出猴子消耗的最大能量。

输入样例
5 3
2 2 2 2 2
3 5 2 1 4
1 3
2 2
4 5

输出样例
12
16
18

Solution

注意到计算消耗能量的式子:\(2(h_x+h_y)+dist(x,y)\),设\(g_i\)为第\(i\)棵树到第一棵树的距离,那么式子可以转化成\(-2h_y+g_y+2h_x-g_x(x<y)\),这样\(x\)\(y\)是不互相影响的,就可以寻找一个区间里\(2h_i+g_i\)的最大值和\(-2h_i-g_i\)的最小值求解。将环复制一遍变成链,用线段树维护即可。

对于一个区间的最大值和最小值可能相同的情况,分别询问最大值对应的次小值和最小值对应的次大值然后取max即可

Code

#include<iostream>
#include<cstdio>
#include<utility>
#define int long long
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N=6e5+5,inf=1e18;
int n,m,d[N],h[N],sum[N];

struct Seg {
#define lc (k<<1)
#define rc (k<<1|1)
#define mid ((l+r)>>1)
	int mx[N<<2],c[N<<2][2],mn[N<<2];

	inline void build (int k,int l,int r) {
		if (l==r) {
			mn[k]=sum[l]-2*h[l];
			mx[k]=sum[l]+2*h[l];
			c[k][0]=c[k][1]=l;
			return;
		}
		build(lc,l,mid);
		build(rc,mid+1,r);
		mx[k]=max(mx[lc],mx[rc]);c[k][0]= mx[lc] > mx[rc] ? c[lc][0] : c[rc][0];
		mn[k]=min(mn[lc],mn[rc]);c[k][1]= mn[lc] < mn[rc] ? c[lc][1] : c[rc][1];
	}
	
	inline pair<int,int> querymx (int k,int l,int r,int x,int y) {
		if (x>y)	return mk(-inf,-inf);
		if (x<=l&&r<=y) return mk(mx[k],c[k][0]);
		pair<int,int> res=mk(-inf,0);
		if (x<=mid)	res=max(res,querymx(lc,l,mid,x,y));
		if (y>mid)	res=max(res,querymx(rc,mid+1,r,x,y));
		return res;
	}

	inline pair<int,int> querymn (int k,int l,int r,int x,int y) {
		if (x>y)	return mk(inf,inf);
		if (x<=l&&r<=y) return mk(mn[k],c[k][1]);
		pair<int,int> res=mk(inf,n*2+1);
		if (x<=mid)	res=min(res,querymn(lc,l,mid,x,y));
		if (y>mid)	res=min(res,querymn(rc,mid+1,r,x,y));
		return res;
	}
#undef lc
#undef rc
#undef mid
}T;

inline int Abs (int x) {return x<0 ? -x : x;}

inline int ask (int x,int y) {
//	cout<<x<<" "<<y<<endl;
	int a=T.querymx(1,1,n<<1,x,y).se;
	int b=T.querymn(1,1,n<<1,x,y).se;//cout<<a<<" "<<b<<endl;
	if (a!=b)	return h[a]*2+h[b]*2+Abs(sum[a]-sum[b]);
	int A,B;
	A=max(T.querymx(1,1,n<<1,x,a-1),T.querymx(1,1,n<<1,a+1,y)).se;
	B=min(T.querymn(1,1,n<<1,x,b-1),T.querymn(1,1,n<<1,b+1,y)).se;
//	cout<<A<<" "<<b<<" "<<a<<" "<<B<<endl;
	return max(h[A]*2+h[b]*2+Abs(sum[A]-sum[b]),h[a]*2+h[B]*2+Abs(sum[a]-sum[B]));
}

signed main () {
	scanf("%lld%lld",&n,&m);
	for (int i=1;i<=n;i++) scanf("%lld",&d[i]),sum[i]=sum[i-1]+d[i-1],d[i+n]=d[i];
	for (int i=1;i<=n;i++) scanf("%lld",&h[i]),sum[i+n]=sum[i+n-1]+d[i+n-1],h[i+n]=h[i];
	T.build(1,1,n<<1);
	while (m--) {
		int a,b;scanf("%lld%lld",&a,&b);
		int ans=0;
		if (a<=b) ans=ask(b+1,a+n-1);
		else ans=ask(b+1,a-1);
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2020-11-15 18:28  蒟蒻zyx_qwq  阅读(110)  评论(0编辑  收藏  举报