-
题目链接
-
前言:
-
最近在做线段树的练习,对于区间合并问题不是很清楚,花了好久才把线段树的区间合并问题理清楚,所以把学习的过程记录下来,建议手动建树并模拟测试用例
-
题目大意:
-
有一个数组,求这个数组中最长的单调连续递增序列的长度
-
题解:
-
见一下注释
#include
using namespace std;
const int N = 100000;
int n,m;
int a[N];
struct node{
int l;
int r;
int lv;
int rv;
int lm;
int rm;
int m;
int mid(){
return (l+r)/2;
}
int len(){
return r-l+1;
}
};
struct Seg{
node tree[N*4];
void pushUp(int i){
tree[i].lm = tree[i*2].lm;
tree[i].rm = tree[i*2+1].rm;
tree[i].lv = tree[i*2].lv;
tree[i].rv = tree[i*2+1].rv;
tree[i].m = max(tree[i*2].m,tree[i*2+1].m);
if(tree[i*2].rv < tree[i*2+1].lv){
if(tree[i*2].len() == tree[i*2].lm){
tree[i].lm += tree[i*2+1].lm;
}
if(tree[i*2+1].len() == tree[i*2+1].rm){
tree[i].rm += tree[i*2].rm;
}
tree[i].m = max(tree[i].m,tree[i*2].rm+tree[i*2+1].lm);
}
tree[i].m = max(tree[i].m,max(tree[i].rm,tree[i].lm));
}
void build(int i,int l,int r){
tree[i].l = l;
tree[i].r = r;
if(l==r){
tree[i].lv = tree[i].rv = a[l];
tree[i].lm = tree[i].rm = tree[i].m = 1;
} else{
int m = tree[i].mid();
build(i*2,l,m);
build(i*2+1,m+1,r);
pushUp(i);
}
}
void update(int i,int pos,int v){
if(tree[i].l == tree[i].r) tree[i].lv = tree[i].rv = v;
else{
int m = tree[i].mid();
if(pos<=m) update(i*2,pos,v);
else update(i*2+1,pos,v);
pushUp(i);
}
}
int query(int i,int l,int r){
int li = tree[i].l;
int ri = tree[i].r;
if(l<=li && ri<=r){
return tree[i].m;
}else{
int m = tree[i].mid();
if(r<=m) return query(i*2,l,r);
else if(l>m) return query(i*2+1,l,r);
else{
int sum1 = 0;
int sum2 = 0;
int m1 = query(i*2,l,r);
int m2 = query(i*2+1,l,r);
if(tree[i*2].rv < tree[i*2+1].lv){
sum1 = min(tree[i*2].rm,m-l+1);
sum2 = min(tree[i*2+1].lm,r-m);
}
return max(max(m1,m2),sum1+sum2);
}
}
}
}seg;
int main(){
int t;
cin>>t;
while(t--){
char str;
int l,r;
scanf("%d%d",&n,&m);
for(int i=0;iscanf("%d",&a[i]);
seg.build(1,0,n-1);
while(m--){
cin>>str>>l>>r;
if(str == 'Q'){
printf("%d\n",seg.query(1,l,r));
}else seg.update(1,l,r);
}
}
return 0;
}
下面再贴一个不带注释的,方便浏览
using namespace std;
const int N = 100000;
int n,m;
int a[N];
struct node{
int l;
int r;
int lv;
int rv;
int lm;//从左到右递增的最大长度
int rm;//从右到左递减的最大长度
int m;
int mid(){
return (l+r)/2;
}
int len(){
return r-l+1;
}
};
struct Seg{
node tree[N*4];
void pushUp(int i){
tree[i].lm = tree[i*2].lm;
tree[i].rm = tree[i*2+1].rm;
tree[i].lv = tree[i*2].lv;
tree[i].rv = tree[i*2+1].rv;
tree[i].m = max(tree[i*2].m,tree[i*2+1].m);
if(tree[i*2].rv < tree[i*2+1].lv){
if(tree[i*2].len() == tree[i*2].lm){
tree[i].lm += tree[i*2+1].lm;//左子区间完全是递增的
}
if(tree[i*2+1].len() == tree[i*2+1].rm){
tree[i].rm += tree[i*2].rm;//右子区间完全是递增
}
tree[i].m = max(tree[i].m,tree[i*2].rm+tree[i*2+1].lm);
}
tree[i].m = max(tree[i].m,max(tree[i].rm,tree[i].lm));
}
void build(int i,int l,int r){
tree[i].l = l;
tree[i].r = r;
if(l==r){
tree[i].lv = tree[i].rv = a[l];//设置左右子树结点得值为a[l]
tree[i].lm = tree[i].rm = tree[i].m = 1;//设置最大长度为1
} else{
int m = tree[i].mid();
build(i*2,l,m);
build(i*2+1,m+1,r);
pushUp(i);
}
}
void update(int i,int pos,int v){
if(tree[i].l == tree[i].r) tree[i].lv = tree[i].rv = v;//是叶子结点,那么就直接更新
else{
int m = tree[i].mid();
if(pos<=m) update(i*2,pos,v);
else update(i*2+1,pos,v);
pushUp(i);
}
}
int query(int i,int l,int r){
//查询[l,r]区间内的最长的连续递增的子序列
int li = tree[i].l;
int ri = tree[i].r;
if(l<=li && ri<=r){
return tree[i].m;//返回最大的长度
}else{
int m = tree[i].mid();
if(r<=m) return query(i*2,l,r);
else if(l>m) return query(i*2+1,l,r);
else{
int sum1 = 0;
int sum2 = 0;
int m1 = query(i*2,l,r);
int m2 = query(i*2+1,l,r);
if(tree[i*2].rv < tree[i*2+1].lv){
sum1 = min(tree[i*2].rm,m-l+1);//防止左子树的右递减的长度大于左子树的区间长度
sum2 = min(tree[i*2+1].lm,r-m);//防止右子树的左递增的长度大于右子树的区间长度
}
return max(max(m1,m2),sum1+sum2);
}
}
}
}seg;
int main(){
int t;
cin>>t;
while(t--){
char str;
int l,r;
scanf("%d%d",&n,&m);
for(int i=0;i"%d",&a[i]);
seg.build(1,0,n-1);
while(m--){
cin>>str>>l>>r;
if(str == 'Q'){
printf("%d\n",seg.query(1,l,r));
}else seg.update(1,l,r);
}
}
return 0;
}
-
注
-
个人拙见,如有更好算法还望指出,谢谢!
-
参考博客:
-
- 线段树题目总结
- hdu 3308 LCIS(单点更新,区间合并)