线段树的学习真的是一件很不容易的事情.
漫长的道路望不到终点...繁杂的思路一时半会理不清T T
LCIS
Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 5759 Accepted Submission(s): 2499
Problem Description
Given n integers.
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].
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].
Input
T in the first line, indicating the case number.
Each case starts with two integers n , m(0<n,m<=10 5).
The next line has n integers(0<=val<=10 5).
The next m lines each has an operation:
U A B(0<=A,n , 0<=B=10 5)
OR
Q A B(0<=A<=B< n).
Each case starts with two integers n , m(0<n,m<=10 5).
The next line has n integers(0<=val<=10 5).
The next m lines each has an operation:
U A B(0<=A,n , 0<=B=10 5)
OR
Q A B(0<=A<=B< n).
Output
For each Q, output the answer.
Sample Input
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
Sample Output
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的操作,pushuup在线段树里边起到几个作用;1.维护树内数据2.向上更新该更新的数据
所以建树的同时 要不断的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用来存的数据是当前区间所求最大值.
}
}
pushup的操作有了之后 ,我们建树的工程也就同时结束了.
接下来谈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);
}
最后再来说query(查询)操作.
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));
}
}
}
}