线段树的学习真的是一件很不容易的事情.
漫长的道路望不到终点...繁杂的思路一时半会理不清T T
1 10 10 7 7 3 3 5 9 9 8 1 8 Q 6 6 U 3 4 Q 0 1 Q 0 5 Q 4 7 Q 3 5 Q 0 2 Q 4 6 U 6 10 Q 0 9
1 1 4 2 3 1 2 5
题目大意:
给出n个数m个操作
U操作 把a位子上的数字改成b
Q操作 问区间(a,b)上最长连续递增子序列长度....(看起来好麻烦的样纸~)
这里先谈一下初始化的问题:
const int maxn=100000+5; int msum[maxn<<2];//区间内最长连续递增子序列长度. int lsum[maxn<<2];//区间从左边第一个数开始算起 到区间最后一个数的连续递增序列长度(比如(1,5)区间内数据:2 3 5 4 1)这个时候这个数组里边存的数据是2(2,3) int rsum[maxn<<2];//右边的被(比如(1,5)区间内数据:5 4 1 2 3)这个时候数组里边存的数据是3(1,2,3) int num[maxn];//存入数据的数组(并不是树哦~)上边这部分的内容一定要理解透彻.
#include<iostream> #include<stdio.h> #include<string.h> using namespace std;//水一发头文件 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1//左右子树的快速调用写法~
int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)//这里输入强调 n个数的数据存入方式....跟树无关哦~. { scanf("%d",&num[i]); }
void build(int l,int r,int rt)//日常建树... { if(l==r) { msum[rt]=lsum[rt]=rsum[rt]=1;//这里自行理解0.0 return ; } int m = (l+r)>>1 ; build(lson) ; build(rson) ; pushup(l,r,rt) ;//主要还是通过建树的操作来印出来很关键的步骤. }
所以建树的同时 要不断的pushup pushup pushup------
这里我们知道lsum和rsum以及msum的含义.所以如果是向上维护数据的话 当前区间lsum就可以递推给上边的lsum既lsum[rt]=lsum[rt*2].
这个时候细心的小伙伴就要问了(假设现在区间是(1,5))如果现在的lsum的值应该是5但是你现在最大的值也就是个3 你这上坟烧报纸烧的略有些明显啊.
既然细心的小伙伴知道现在最大的值也就是个3 这个时候我们呢 就要找到3这个罚分点 然后判断是否能够区间合并 完成3->5的操作.
这里贴上pushup的操作代码加以详解:
void pushup(int l,int r,int rt)//((1,5),2)区间(l,r)节点rt //以下配合上边所述的例子来走的. { lsum[rt]=lsum[rt*2]; rsum[rt]=rsum[rt*2+1]; msum[rt]=max(msum[rt*2],msum[rt*2+1]);//上边的语言描述尽我所能的解释了0.0 int m=(l+r)/2;//罚分点. int len=r-l+1;//区间长度 if(num[m]<num[m+1])//如果能有3->5的操作(也可以理解为能够区间合并) { if(lsum[rt]==len-(len/2))//首先我们要满足3的存在 lsum[rt]+=lsum[rt*2+1];//这个时候我们就获得5的存在了 if(rsum[rt]==len/2)//当然也有肯能是右边的3->5 rsum[rt]+=rsum[rt*2];//我们不能只看左边而忘记了右边~ msum[rt]=max(msum[rt],lsum[rt*2+1]+rsum[rt*2]);//msum用来存的数据是当前区间所求最大值. } }
接下来谈update(也就是U操作)
这部分还是比较好理解的 这里直接贴上代码和详解:
void update(int p,int l,int r,int rt)//找到p之前不断的pushup.//修改数据的操作在主函数中已经搞定了. { if(l==r) return ; int m=(l+r)/2; if(p<=m) update(p,lson); else update(p,rson); pushup(l,r,rt); }
int query(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R) { return msum[rt]; } int m=(l+r)>>1; if(R<=m)return query(L,R,lson); if(L>m)return query(L,R,rson);//左右区间的寻找. int a,b; a=query(L,R,lson); b=query(L,R,rson); int ans; ans=max(a,b);//ans首先取左右区间最大值. if(num[m]<num[m+1])//这里还有一个合并区间的问题. { int c; c=min(rsum[rt*2],m-L+1)+min(lsum[rt*2+1],R-m);//这里在纸上描描画画就懂咯~ ans=max(c,ans); } return ans; }
蓝后我们这里贴上完整的AC代码
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 const int maxn=100000+5; int msum[maxn<<2]; int lsum[maxn<<2]; int rsum[maxn<<2]; int num[maxn]; void pushup(int l,int r,int rt) { lsum[rt]=lsum[rt*2]; rsum[rt]=rsum[rt*2+1]; msum[rt]=max(msum[rt*2],msum[rt*2+1]); int m=(l+r)/2; int len=r-l+1; if(num[m]<num[m+1]) { if(lsum[rt]==len-(len/2)) lsum[rt]+=lsum[rt*2+1]; if(rsum[rt]==len/2) rsum[rt]+=rsum[rt*2]; msum[rt]=max(msum[rt],lsum[rt*2+1]+rsum[rt*2]); } } void build(int l,int r,int rt) { if(l==r) { msum[rt]=lsum[rt]=rsum[rt]=1; return ; } int m = (l+r)>>1 ; build(lson) ; build(rson) ; pushup(l,r,rt) ; } void update(int p,int l,int r,int rt)//找到p之前不断的pushup. { if(l==r) return ; int m=(l+r)/2; if(p<=m) update(p,lson); else update(p,rson); pushup(l,r,rt); } int query(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R) { return msum[rt]; } int m=(l+r)>>1; if(R<=m)return query(L,R,lson); if(L>m)return query(L,R,rson); int a,b; a=query(L,R,lson); b=query(L,R,rson); int ans; ans=max(a,b); if(num[m]<num[m+1]) { int c; c=min(rsum[rt*2],m-L+1)+min(lsum[rt*2+1],R-m);//防炸专用. ans=max(c,ans); } return ans; } int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); } build(1,n,1); while(m--) { char op[121]; scanf("%s",op); int a,b; scanf("%d%d",&a,&b); if(op[0]=='U') { a++; num[a]=b; update(a,1,n,1); } else { a++; b++; printf("%d\n",query(a,b,1,n,1)); } } } }