↓ 跳过正文
  1. Posts/

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

·886 字·2 分钟

题目链接

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

思路:

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}

相关文章

codeforces #351 D. Jeff and Removing Periods (线段树/树状数组判断位置成等差数列)

·1339 字·3 分钟
题目链接 题意:有 n 个数,每次可以删除掉数值相同并且所在位置成等差数列(只删 2 个数或者只删 1 个数应该也是可以的),删掉这些数以后可以将剩下的数重新以任意顺序排列,称为一次操作。现在给出 m 个询问,每个询问一个区间 [l,r],问删光区间 [l,r] 中的数最少需要的操作次数。

spoj DQUERY - D-query (询问区间中不同数的个数,线段树(离线) or 莫队算法(离线) or 主席树(在线))

题目链接 题意:给出 n 个数,然后 m 个询问,每个询问一个区间 [l,r],问该区间中不同的数有多少个。 思路:离线处理+线段树的做法不多说了: 代码实现 1/* *********************************************** 2Author :111qqz 3Created Time :Fri 16 Sep 2016 11:34:32 PM CST 4File Name :code/spoj/dquery.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 26using namespace std; 27const double eps = 1E-8; 28const int dx4[4]={1,0,0,-1}; 29const int dy4[4]={0,-1,1,0}; 30const int inf = 0x3f3f3f3f; 31const int N=3E4+7; 32const int M=2E5+7; 33int n,Q; 34int a[N]; 35int tree[N<<2]; 36map<int,int>mp; 37struct node 38{ 39 int l,r; 40 int id; 41 bool operator < (node b)const 42 { 43 if (r==b.r) return l<b.l; 44 return r<b.r; 45 } 46}q[M]; 47void PushUp( int rt) 48{ 49 tree[rt] = tree[rt<<1] + tree[rt<<1|1]; 50} 51void update( int p,int sc,int l,int r ,int rt) 52{ 53 if (l==r) 54 { 55 tree[rt]+=sc; 56 return; 57 } 58 int m = (l+r)>>1; 59 if (p<=m) update(p,sc,lson); 60 else update(p,sc,rson); 61 PushUp(rt); 62} 63int query(int L,int R,int l,int r,int rt) 64{ 65 if (L<=l&&r<=R) return tree[rt]; 66 int m = (l+r)>>1; 67 int ret = 0 ; 68 if (L<=m) ret += query(L,R,lson); 69 if (R>=m+1) ret+=query(L,R,rson); 70 return ret; 71} 72int ans[M]; 73int main() 74{ 75 #ifndef ONLINE_JUDGE 76 freopen("code/in.txt","r",stdin); 77 #endif 78 cin>>n; 79 for ( int i = 1 ; i <= n ; i++) scanf("%d",&a[i]); 80 cin>>Q; 81 for ( int i = 1 ; i <= Q ; i++) scanf("%d %d",&q[i].l,&q[i].r),q[i].id = i ; 82 sort(q+1,q+Q+1); 83 int cur = 1; 84 for ( int i = 1 ; i <= Q ; i++) 85 { 86 for ( ; cur <= q[i].r ; cur++) 87 { 88 if (mp[a[cur]]) update(mp[a[cur]],-1,1,n,1); 89 mp[a[cur]] = cur; 90 update(mp[a[cur]],1,1,n,1); 91 } 92 ans[q[i].id] = query(q[i].l,q[i].r,1,n,1); 93 } 94 for ( int i = 1 ; i <= Q ; i++) printf("%d\n",ans[i]); 95 #ifndef ONLINE_JUDGE 96 fclose(stdin); 97 #endif 98 return 0; 99} 之后补一个主席树的做法

codeforces 338 E. Optimize! (线段树维护最小前缀和)

·1308 字·3 分钟
题目链接 题意:题意是由伪代码给出的,手算模拟了一下(noip 初赛即视感),题意大概是说,给出两个数组 a 和 b,a 数组长度为 n,b 数组长度为 len,然后从 a 中截取连续的 len 个元素,称为数组 s,如果存在一种方法使得 s 中元素和 b 中的元素一一对应且每组和都大于等于 h,则称这个 s 是合法的。现在问 a 中有多少个合法的 s。 具体来说,对于样例 5 2 10 5 3 1 8 5 5 7

BZOJ 1756: Vijos1083 小白逛公园  (线段树维护单点修改区间查询最大子段和)

·1113 字·3 分钟
1756: Vijos1083 小白逛公园 # Time Limit: 10 Sec Memory Limit: 64 MB Submit: 1078 Solved: 353 [Submit][Status][Discuss] Description # 小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。 一开始,小白就根据公园的风景给每个公园打了分-.-。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a、b两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。 那么,就请你来帮小白选择公园吧。

hit oj 2687 Candy (线段树动态维护最大连续子段)

·627 字·2 分钟
题目链接 题意:给出n个数,m个修改,每次修改后询问整个区间的最大连续子段。 思路:考虑一段区间,分成左右两个子区间,这段区间的最大子段有三种情况:只在左区间中,只在右区间中,既在左区间中又在右区间中。前两种很好维护,对于后一种,我们新增加线段树的两个域,mxl,mxr,分别表示一个区间中包含左端点在的最大字段和(也就是最大前缀和),和一个区间中包含右端点在的最大子段和(也就是最大后缀和),然后对于最大子段既在左区间又在右区间的情况,只需要合并【左区间的最大后缀和 】和【右区间的最大前缀和】就好。