线段树——51nod1593&CF515E 公园晨跑
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\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;
}