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}