HDU 3308 LCIS(线段树:单点更新,求最大连续子串)
http://acm.hdu.edu.cn/showproblem.php?pid=3308
You have two operations:
U A B: replace the Ath number by B. (index counting from 0)
Q A B: output the length of the longest consecutive increasing subsequence (LCIS) in [a, b].
Each case starts with two integers n , m(0<n,m<=105).
The next line has n integers(0<=val<=105).
The next m lines each has an operation:
U A B(0<=A,n , 0<=B=105)
OR
Q A B(0<=A<=B< n).
分析:
首先题目中找的递增序列是严格增加的,如果有7,7这样的序列不算递增.其次本题是单点更新,所以不用PushDown,只用PushUp就行.
线段树维护信息:val(表节点的值,只有子节点有用,这个信息也可以不维护的),sub(最长上升子序列的长度),pre(最长上升前缀的长度),suf(最长上升后缀的长度).
由于一个区间[L,R]内的LCIS,可能在左半边,也可能跨越两边,也可能在右半边,所以对于任意区间的LCIS查询需要递归查询左右儿子的LCIS并且还要递归查询左儿子的最长上升后缀和右儿子的最长上升前缀.所以本题需要3个查询操作.
PushUp(i,num)操作: 根据儿子节点的sub,pre,suf来更新父节点的sub,pre,suf.
build(i,l,r)操作: 递归建树.最后需要PushUp更新父节点.
update(q,v,i,l,r)操作: 递归到叶节点更新sub,pre,suf,然后在PushUp更新所有父节点的sub,pre,suf.
query_pre(ql,qr,i,l,r)操作 :该操作的含义是返回区间[ql,qr]与[l,r]相交部分的最长前缀上升子序列长度. 即如果ql<=l<=qr的话,所指的序列就是以l开头的,否则如果l<ql, 所指的序列就是以ql开头的最长前缀序列. 那么以ql开头的如何求呢? 这个需要首先定位到那个[l,r]其中l正好==ql,然后求该节点的pre,看看pre是否==r-l+1,如果等于的话可以往后继续延伸,最多延伸到qr.
query_suf(ql,qr,i,l,r)操作 : 该操作的含义是返回区间[ql,qr]与[l,r]相交部分的最长后缀上升子序列长度.如果r<=qr,那么直接返回suf[i]即可.否则如果r>qr的话, 需要定位到这样的j节点,它控制的区间[L,R]中的R正好==qr,然后求suf[j],如果suf[j]==R-L+1,那么就可以继续向前延伸.
query(ql,qr,i,l,r)操作: 该操作的含义是返回区间[ql,qr]与[l,r]相交部分的最长上升子序列长度.如果[ql,qr]与[l,r]左儿子有相交就查左边的,如果右边有相交,还需要查右边的,如果都有相交,还需要综合考虑跨越了左儿子和右儿子的,即以左儿子的最长上升后缀,右儿子的最长上升前缀组成的.
AC代码:一次AC
<span style="font-size:18px;">#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
#define root 1,0,n-1
const int MAXN = 100000 + 100;
int val[MAXN];
int sub[MAXN * 4], pre[MAXN * 4], suf[MAXN * 4];
//统计下层信息到i节点,需要更新sub,pre,suf三信息
void PushUp(int i, int l, int r)
{
int m = (l + r) / 2;
sub[i] = max(sub[i * 2], sub[i * 2 + 1]); //更新sub
if(val[m] < val[m + 1]) sub[i] = max(sub[i], suf[i * 2] + pre[i * 2 + 1]);
pre[i] = pre[i * 2]; //更新pre
if(pre[i] == m - l + 1 && val[m] < val[m + 1]) pre[i] += pre[i * 2 + 1];
suf[i] = suf[i * 2 + 1]; //更新suf
if(suf[i] == r - m && val[m] < val[m + 1]) suf[i] += suf[i * 2];
}
//建立线段树且更新所有节点的sub,suf,pre
void build(int i, int l, int r)
{
if(l == r)
{
sub[i] = pre[i] = suf[i] = 1;
return ;
}
int m = (r + l) / 2;
build(lson);
build(rson);
PushUp(i, l, r);
}
//将q号元素的值置换为v
void update(int q, int v, int i, int l, int r)
{
if(l == r)
{
val[l] = v;
sub[i] = pre[i] = suf[i] = 1;
return ;
}
int m = (r + l) / 2;
if(q <= m) update(q, v, lson);
else update(q, v, rson);
PushUp(i, l, r);
}
//返回区间[ql,qr]与[l,r]最大目标前缀长
//分四种情况处理
//[ql,qr]包含[l,r]
//[ql,qr]只与[l,m]相交
//[ql,qr]只与[m+1,r]相交
//[ql,qr]与[l,m]且[m+1,r]都相交
int query_pre(int ql, int qr, int i, int l, int r)
{
if(ql <= l && r <= qr)//[ql,qr]包含[l,r]
return pre[i];
int m = (r + l) / 2;
if(qr <= m) return query_pre(ql, qr, lson);//[ql,qr]只与[l,m]相交
if(m < ql) return query_pre(ql, qr, rson); //[ql,qr]只与[m+1,r]相交
//[ql,qr]与[l,m]且[m+1,r]都相交
int L = query_pre(ql, qr, lson); //先求左边的前缀长
//特别注意这下面的max函数的意义,本函数取的是公共区间
if(L == m - max(l,ql) + 1 && val[m] < val[m + 1]) L += query_pre(ql, qr, rson);
return L;
}
//返回区间[ql,qr]与[l,r]最大目标后缀长
//分四种情况处理
//[ql,qr]包含[l,r]
//[ql,qr]只与[l,m]相交
//[ql,qr]只与[m+1,r]相交
//[ql,qr]与[l,m]且[m+1,r]都相交
int query_suf(int ql, int qr, int i, int l, int r)
{
if(ql <= l && r <= qr)
return suf[i];
int m = (l + r) / 2;
if(qr <= m) return query_suf(ql, qr, lson);
if(m < ql) return query_suf(ql, qr, rson);
int R = query_suf(ql, qr, rson);
//特别注意这下面的min函数的意义,本函数取的是公共区间
if(R == min(r,qr) - m && val[m] < val[m + 1]) R += query_suf(ql, qr, lson);
return R;
}
//返回区间[ql,qr]与[l,r]最大目标子串长
//分四种情况处理
//[ql,qr]包含[l,r]
//[ql,qr]只与[l,m]相交
//[ql,qr]只与[m+1,r]相交
//[ql,qr]与[l,m]且[m+1,r]都相交
int query(int ql,int qr,int i,int l,int r)
{
if(ql<=l && r<=qr)
{
return sub[i];
}
int m=(l+r)/2;
if(qr<=m) return query(ql,qr,lson);
else if(m<ql) return query(ql,qr,rson);
int res=0;
res=max(query(ql,qr,lson),query(ql,qr,rson));
int pre=query_pre(ql,qr,rson);
int suf=query_suf(ql,qr,lson);
if(val[m]<val[m+1]) res=max(res,pre+suf);
return res;
}
int main()
{
int T, n, m;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++)
scanf("%d", &val[i]);
build(root);
while(m--)
{
char str[10];
int x, y;
scanf("%s%d%d", str, &x, &y);
if(str[0] == 'U')
update(x, y, root);
else if(str[0] == 'Q')
printf("%d\n", query(x, y, root));
}
}
return 0;
}
</span>
AC代码:不实现query_pre函数和query_suf函数的代码
<span style="font-size:18px;">#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
#define root 1,0,n-1
const int MAXN = 100000 + 100;
int val[MAXN];
int sub[MAXN * 4], pre[MAXN * 4], suf[MAXN * 4];
//统计下层信息到i节点,需要更新sub,pre,suf三信息
void PushUp(int i, int l, int r)
{
int m = (l + r) / 2;
sub[i] = max(sub[i * 2], sub[i * 2 + 1]); //更新sub
if(val[m] < val[m + 1]) sub[i] = max(sub[i], suf[i * 2] + pre[i * 2 + 1]);
pre[i] = pre[i * 2]; //更新pre
if(pre[i] == m - l + 1 && val[m] < val[m + 1]) pre[i] += pre[i * 2 + 1];
suf[i] = suf[i * 2 + 1]; //更新suf
if(suf[i] == r - m && val[m] < val[m + 1]) suf[i] += suf[i * 2];
}
//建立线段树且更新所有节点的sub,suf,pre
void build(int i, int l, int r)
{
if(l == r)
{
sub[i] = pre[i] = suf[i] = 1;
return ;
}
int m = (r + l) / 2;
build(lson);
build(rson);
PushUp(i, l, r);
}
//将q号元素的值置换为v
void update(int q, int v, int i, int l, int r)
{
if(l == r)
{
val[l] = v;
sub[i] = pre[i] = suf[i] = 1;
return ;
}
int m = (r + l) / 2;
if(q <= m) update(q, v, lson);
else update(q, v, rson);
PushUp(i, l, r);
}
//返回区间[ql,qr]与[l,r]最大目标子串长
//分四种情况处理
//[ql,qr]包含[l,r]
//[ql,qr]只与[l,m]相交
//[ql,qr]只与[m+1,r]相交
//[ql,qr]与[l,m]且[m+1,r]都相交
int query(int ql,int qr,int i,int l,int r)
{
if(ql<=l && r<=qr)
{
return sub[i];
}
int m=(l+r)/2;
if(qr<=m) return query(ql,qr,lson);
else if(m<ql) return query(ql,qr,rson);
int res=max(query(ql,qr,lson),query(ql,qr,rson));
int prex=min(qr-m, pre[i*2+1]);//[m+1,qr]与[m+1,r]相交部分的最大前缀
int sufx=min(m-ql+1, suf[i*2]);//[ql,m]与[l,m]的最大后缀
if(val[m]<val[m+1]) res=max(res,prex+sufx);
return res;
}
int main()
{
int T, n, m;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++)
scanf("%d", &val[i]);
build(root);
while(m--)
{
char str[10];
int x, y;
scanf("%s%d%d", str, &x, &y);
if(str[0] == 'U')
update(x, y, root);
else if(str[0] == 'Q')
printf("%d\n", query(x, y, root));
}
}
return 0;
}
</span>