codeforces 515 E. Drazil and Park ( 线段树区间合并)

题目链接

题意:圆上,询问任意一段弧中,任意两点的距离+两点的权值和的最大值。

思路:

1.环先拆成串,复制1..n到后面,变成1..2n。

化简公式:

2 * h[u] + 2 * h[v] + dist(u, v) = 2 * h[v] + d[1] + d[2] + ... + d[v-1] + 2 * h[u] - (d[1] + d[2] + ... + d[u-1]).

设A[v] = 2 * h[v] + d[1] + d[2] + ... + d[v-1], B[u] = 2 * h[u] - (d[1] + d[2] + ... + d[u-1]).

Another important thing is that L__u + R__v always bigger than L__v + R__u when u < v.

So we can almost solve the problem just by finding the maximum value of L__u and R__v by RMQ separately and sum them up.

但是至少由两颗树。。。所以要保证u!=v...

这也是本题的难点。。。

做法是,线段树维护三个域。

分别表示某区间A的最大值,某区间B的最大值,某区间A+B的最大值(且保证A和B不属于同一个区间)

如何保证呢? 每次合并的时候。。。合并不同的区间就好了。。。

具体见代码

注意体会这种区间合并的思想。。。

以及query的时候。。。做法类似于:bzoj1756题目链接

/* ***********************************************
Author :111qqz
Created Time :Mon 19 Sep 2016 04:35:20 PM CST
File Name :code/cf/problem/515e.cpp
************************************************ */
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define fst first
#define sec second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ms(a,x) memset(a,x,sizeof(a))
typedef long long LL;
#define pi pair < int ,int >
#define MP make_pair
#define  INF (~0ull>>1)
using namespace std;
const double eps = 1E-8;
const int dx4[4]={1,0,0,-1};
const int dy4[4]={0,-1,1,0};
const int inf = 0x3f3f3f3f;
const int N=2E5+7;
int n,m;
LL d[N],h[N];
struct node
{
    LL mx; //mx保存的是不相同区间的lx+mx的最大值。目的是避免u==v的情况。注意体会这种区间合并。
    LL lx;
    LL rx;
}tree[N<<2];
void PushUp(int rt)
{
    tree[rt].lx = max(tree[rt<<1].lx,tree[rt<<1|1].lx);
    tree[rt].rx = max(tree[rt<<1].rx,tree[rt<<1|1].rx);
    tree[rt].mx = max(tree[rt<<1].lx+tree[rt<<1|1].rx,max(tree[rt<<1].mx,tree[rt<<1|1].mx));
}
void build( int l,int r,int rt)
{
    if (l==r)
    {
	tree[rt].mx = 0;
	tree[rt].lx = 2*h[l]-d[l-1]; //注意到lx可能为负。。。因为初始的时候要为负无穷。。。。并且负无穷要足够小。。。
	tree[rt].rx = 2*h[l]+d[l-1];
	return;
    }
    int m =  (l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}
node query(int L,int R,int l,int r,int rt)
{
    if (L<=l&&r<=R) return tree[rt];
    int m = (l+r)>>1;
    node null;
    null.mx = 0 ;
    null.lx = -INF;
    null.rx = -INF;
    node resl,resr,res;
    resl=resr=res=null;
    if (L<=m) resl = query(L,R,lson);
    if (R>=m+1) resr = query(L,R,rson);
    if (resl.lx==-INF) //只有一半有结果,那么直接更新这一半的结果到res
    {
	res.lx = max(res.lx,resr.lx);
	res.rx = max(res.rx,resr.rx);
	res.mx = max(res.mx,resr.mx);
    }else if (resr.lx==-INF)
    {
	res.lx = max(res.lx,resl.lx);
	res.rx = max(res.rx,resl.rx);
	res.mx = max(res.mx,resl.mx);
    }
    else //两个区间都有结果。。要合并区间。。。。类似于pushup操作
    {
	res.mx = max(resl.mx,resr.mx);
	res.lx = max(resl.lx,resr.lx);
	res.rx = max(resl.rx,resr.rx);
	res.mx = max(resl.lx+resr.rx,res.mx);
    }
    return res;
}
int main()
{
	#ifndef  ONLINE_JUDGE 
	freopen("code/in.txt","r",stdin);
  #endif
    	ios::sync_with_stdio(false);
	cin>>n>>m;
	for ( int i = 1 ; i <= n ; i++) cin>>d[i],d[i+n] = d[i];
	for ( int i = 1 ; i <= n ; i++) cin>>h[i],h[i+n] = h[i];
	for ( int i = 1 ; i <= 2*n ; i++) d[i] = d[i-1] + d[i]; //处理成前缀和.
	build(1,n<<1,1);
	while (m--)
	{
	    int x,y;
	    cin>>x>>y;
	    node ans;
	    if (x<=y)
		ans = query(y+1,x+n-1,1,n<<1,1);
	    else ans = query(y+1,x-1,1,n<<1,1);
	    cout<<ans.mx<<endl;
	}
  #ifndef ONLINE_JUDGE  
  fclose(stdin);
  #endif
    return 0;
}