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题目链接

  1/* ***********************************************
  2Author :111qqz
  3Created Time :Mon 19 Sep 2016 04:35:20 PM CST
  4File Name :code/cf/problem/515e.cpp
  5************************************************ */
  6#include <cstdio>
  7#include <cstring>
  8#include <iostream>
  9#include <algorithm>
 10#include <vector>
 11#include <queue>
 12#include <set>
 13#include <map>
 14#include <string>
 15#include <cmath>
 16#include <cstdlib>
 17#include <ctime>
 18#define fst first
 19#define sec second
 20#define lson l,m,rt<<1
 21#define rson m+1,r,rt<<1|1
 22#define ms(a,x) memset(a,x,sizeof(a))
 23typedef long long LL;
 24#define pi pair < int ,int >
 25#define MP make_pair
 26#define  INF (~0ull>>1)
 27using namespace std;
 28const double eps = 1E-8;
 29const int dx4[4]={1,0,0,-1};
 30const int dy4[4]={0,-1,1,0};
 31const int inf = 0x3f3f3f3f;
 32const int N=2E5+7;
 33int n,m;
 34LL d[N],h[N];
 35struct node
 36{
 37    LL mx; //mx保存的是不相同区间的lx+mx的最大值。目的是避免u==v的情况。注意体会这种区间合并。
 38    LL lx;
 39    LL rx;
 40}tree[N<<2];
 41void PushUp(int rt)
 42{
 43    tree[rt].lx = max(tree[rt<<1].lx,tree[rt<<1|1].lx);
 44    tree[rt].rx = max(tree[rt<<1].rx,tree[rt<<1|1].rx);
 45    tree[rt].mx = max(tree[rt<<1].lx+tree[rt<<1|1].rx,max(tree[rt<<1].mx,tree[rt<<1|1].mx));
 46}
 47void build( int l,int r,int rt)
 48{
 49    if (l==r)
 50    {
 51	tree[rt].mx = 0;
 52	tree[rt].lx = 2*h[l]-d[l-1]; //注意到lx可能为负。。。因为初始的时候要为负无穷。。。。并且负无穷要足够小。。。
 53	tree[rt].rx = 2*h[l]+d[l-1];
 54	return;
 55    }
 56    int m =  (l+r)>>1;
 57    build(lson);
 58    build(rson);
 59    PushUp(rt);
 60}
 61node query(int L,int R,int l,int r,int rt)
 62{
 63    if (L<=l&&r<=R) return tree[rt];
 64    int m = (l+r)>>1;
 65    node null;
 66    null.mx = 0 ;
 67    null.lx = -INF;
 68    null.rx = -INF;
 69    node resl,resr,res;
 70    resl=resr=res=null;
 71    if (L<=m) resl = query(L,R,lson);
 72    if (R>=m+1) resr = query(L,R,rson);
 73    if (resl.lx==-INF) //只有一半有结果,那么直接更新这一半的结果到res
 74    {
 75	res.lx = max(res.lx,resr.lx);
 76	res.rx = max(res.rx,resr.rx);
 77	res.mx = max(res.mx,resr.mx);
 78    }else if (resr.lx==-INF)
 79    {
 80	res.lx = max(res.lx,resl.lx);
 81	res.rx = max(res.rx,resl.rx);
 82	res.mx = max(res.mx,resl.mx);
 83    }
 84    else //两个区间都有结果。。要合并区间。。。。类似于pushup操作
 85    {
 86	res.mx = max(resl.mx,resr.mx);
 87	res.lx = max(resl.lx,resr.lx);
 88	res.rx = max(resl.rx,resr.rx);
 89	res.mx = max(resl.lx+resr.rx,res.mx);
 90    }
 91    return res;
 92}
 93int main()
 94{
 95	#ifndef  ONLINE_JUDGE 
 96	freopen("code/in.txt","r",stdin);
 97  #endif
 98    	ios::sync_with_stdio(false);
 99	cin>>n>>m;
100	for ( int i = 1 ; i <= n ; i++) cin>>d[i],d[i+n] = d[i];
101	for ( int i = 1 ; i <= n ; i++) cin>>h[i],h[i+n] = h[i];
102	for ( int i = 1 ; i <= 2*n ; i++) d[i] = d[i-1] + d[i]; //处理成前缀和.
103	build(1,n<<1,1);
104	while (m--)
105	{
106	    int x,y;
107	    cin>>x>>y;
108	    node ans;
109	    if (x<=y)
110		ans = query(y+1,x+n-1,1,n<<1,1);
111	    else ans = query(y+1,x-1,1,n<<1,1);
112	    cout<<ans.mx<<endl;
113	}
114  #ifndef ONLINE_JUDGE  
115  fclose(stdin);
116  #endif
117    return 0;
118}