CF515E Drazil and Park

题面翻译

有一只猴子,他生活在一个环形的公园里。有 \(n\) 棵树围绕着公园。第 \(i\) 棵树和第 \(i+1\) 棵树之间的距离是 \(d_i\),而第n棵树和第一棵树之间的距离是 \(d_n\),第i棵树的高度是 \(h_i\)

这只猴子每天要进行晨跑。晨跑的步骤如下:

· 他先选择两棵树;

· 然后爬上第一棵树;

· 再从第一棵树上下来,接着围绕着公园跑(有两个可能的方向)到第二棵树,然后爬上第二棵树;

· 最后从第二棵树上下来。

但是有一些小孩会在连续的一些树上玩耍。所以猴子不能经过这些树。

比如现在猴子选择的第\(x\)棵和第\(y\)棵树,那么该早晨他消耗的能量是 \(2(hx+hy)+dist(x,y)\) 。由于某一条路径是被小孩子占据的,所以他只能跑另外一条,因此 \(dist(x,y)\) 是确定的。

现在给出第i天,孩子们会在第 \(a_i\) 棵树和 bi 棵树之间玩耍。具体的,如果 \(ai≤bi\) ,那么孩子玩耍的区间就是 \([ai,bi]\) ,否则孩子玩耍的区间就是 \([ai,n]⋃ [1,bi]\)

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

样例 #1

样例输入 #1

5 3
2 2 2 2 2
3 5 2 1 4
1 3
2 2
4 5

样例输出 #1

12
16
18

样例 #2

样例输入 #2

3 3
5 1 4
5 1 4
3 3
2 2
1 1

样例输出 #2

17
22
11

分析

首先化环为链,设 \(s[]\)\(d[]\) 的前缀和,则 \(dis(x,y)=s[y]-s[x]\)

题目要求 \(2(h_x+h_y)+dis(x,y)\) 最大,则 \(2h_x+2h_y+s[y]-s[x]\) 最大

则有 \(2h_y+s[y]+(2h_x-s[x])\) 最大,也就是让 \(2h_y+s[y]\) 最大,\(2h_x-s[x]\) 最小,线段树维护两个答案即可。

但是需要考虑如果查询出来的两个位置 \(pos\) 相等,需要在 \([l,pos-1],[pos+1,r]\) 区间内再查一次最大最小值即可。

同时注意到刚才我们要查的是位置,所以维护 RMQ 信息的时候要维护的是 RMQ 的下标。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,sum;
int d[700005],h[700005],maxx[300005<<2],minn[300005<<2],sum1[700005],sum2[700005];
int getmax(int x,int y){return sum1[x]>sum1[y]?x:y;}
int getmin(int x,int y){return sum2[x]<sum2[y]?x:y;}
void pushup(int num){
	maxx[num]=getmax(maxx[num<<1],maxx[num<<1|1]);
	minn[num]=getmin(minn[num<<1],minn[num<<1|1]);
}
void build(int l,int r,int num){
	if(l==r){
		maxx[num]=minn[num]=l;
		return;
	}
	int mid=l+r>>1;
	build(l,mid,num<<1),build(mid+1,r,num<<1|1);
	pushup(num);
}
int querymax(int l,int r,int num,int x,int y){
	if(x>r||y<l)return 700003;
	if(x<=l&&r<=y)return maxx[num];
	int mid=l+r>>1;
	return getmax(querymax(l,mid,num<<1,x,y),querymax(mid+1,r,num<<1|1,x,y));
}
int querymin(int l,int r,int num,int x,int y){
	if(x>r||y<l)return 700003;
	if(x<=l&&r<=y)return minn[num];
	int mid=l+r>>1;
	return getmin(querymin(l,mid,num<<1,x,y),querymin(mid+1,r,num<<1|1,x,y));
}
int calcmax(int l,int r){return l>r?0:querymax(1,n<<1|1,1,l,r);}
int calcmin(int l,int r){return l>r?0:querymin(1,n<<1|1,1,l,r);}
int solve(int l,int r){
	int maxxx=calcmax(l,r),minnn=calcmin(l,r);
	if(maxxx==minnn){
		int maxxxx=getmax(calcmax(l,maxxx-1),calcmax(maxxx+1,r));
		int minnnn=getmin(calcmin(l,minnn-1),calcmin(minnn+1,r));
		return max(sum1[maxxxx]-sum2[minnn],sum1[maxxx]-sum2[minnnn]);
	}
	return sum1[maxxx]-sum2[minnn];
}
signed main(){
	cin>>n>>m;sum1[700003]=-LONG_LONG_MAX,sum2[700003]=LONG_LONG_MAX;
	for(int i=1;i<=n;i++)cin>>d[i%n+1],d[i%n+1+n]=d[i%n+1];
	for(int i=1;i<=n;i++)cin>>h[i],h[i+n]=h[i];
	sum1[0]=-LONG_LONG_MAX,sum2[0]=LONG_LONG_MAX;
	for(int i=1;i<=(n<<1|1);i++){
		sum+=d[i];
		sum1[i]=sum+(h[i]<<1),sum2[i]=sum-(h[i]<<1);
	}
	build(1,n<<1|1,1);
	for(int i=1,l,r;i<=m;i++){
		cin>>l>>r;
		if(l<=r)cout<<solve(r+1,l-1+n)<<endl;
		else cout<<solve(r+1,l-1)<<endl;
	}
	return 0;
}
posted @ 2023-06-24 11:45  alex_liu09  阅读(15)  评论(0)    收藏  举报