题目链接:https://www.nowcoder.com/acm/contest/158/B
给你一个长度为 n 的序列 a ,求最长的连续的严格上升区间的长度。
同时会进行 m 次修改,给定 x , y ,表示将 ax 修改为 y ,每次修改之后都要求输出答案。、
思路:开始拿到题准备用线段树的,维护区间的最长连续上升区间,但是区间合并时无法维护。后来NYX在网上找到了模板,线段树做的, (还是要多做题),当时这道题A了,自己也准备研究别人怎么维护的,因为中间去学习莫比乌斯反演了,所以一直拖到今天。
今天认真研究了别人的代码(看没有注释的代码简直是程序员的***),看懂了大概思路,维护左端为开头的最长连续上升区间的长度ls,右端为结尾的最长连续上升区间的长度rs,本区间的最长连续上升区间的长度s
思考:每做一道题,就觉得对线段树的理解多了一层,自己觉得无法维护的,也是可以维护的,还是题做的太少了,努力刷题,努力进步
//区间合并维护的核心代码
node[1].s=max(node[2].s, node[3].s);
node[1].ls=node[2].ls;
node[1].rs=node[3].rs;
if(a<b)//区间合并处可能产生新的连续上升区间
{
node[1].s=(node[1].s, node[2].rs+node[3].ls);//新产生连续区间的长度 node[2].rs+node[3].ls
//因为node[2], node[3]都可能是整体连续上升的
//所以要判断是否更新node[1].ls, node[1].rs
int len=node[fi].r-node[fi].l+1;//本区间长度
int len_r=len/2; //右子区间长度
int len_l=len-len_r; //左子区间长度
if(node[1].ls==len_l)//左子区间整体上升
node[1].ls+=node[3].ls;
if(node[fi].rs==len_r)//右子区间整体上升
node[1].rs+=node[2].rs;
}
因为我的模板把区间的元素存在结构体中,所以还要维护区间最左边,最右边的元素,所以就重新写了模板,把元素存在一个数组中,这样就可以直接访问了
int mid=(node[i].l+node[i].r)/2;
a[mid] //左子区间的最右元素a
a[mid+1] //右子区间的最左元素b
代码查询还写了区间查询,而这题查询整个区间,推荐另外一题查询区间的最长的连续的严格上升区间:http://acm.hdu.edu.cn/showproblem.php?pid=3308
#include <bits/stdc++.h>
using namespace std;
#define MAX_NODE 800000
#define MAX 200000
int Max=1<<31;
struct NODE{
int s, ls, rs;//信息
int l, r;//区间[l, r]
}node[MAX_NODE];
int f[MAX];//记录节点i的node结构体数组下标f[i]
int a[MAX];
void js(int i, int l, int r)
{
node[i].l=l;
node[i].r=r;
node[i].ls=1;
node[i].rs=1;
node[i].s=1;
if(l==r)
{
f[l]=i;
return;
}
int mid=(l+r)/2;
js(i*2, l, mid);
js(i*2+1, mid+1, r);
}
void gx(int ri)
{
if(ri==1)
return;
int fi=ri/2;
node[fi].s=max(node[fi*2].s, node[fi*2+1].s);
node[fi].ls=node[fi*2].ls;
node[fi].rs=node[fi*2+1].rs;
int mid=(node[fi].l+node[fi].r)/2;
int len=node[fi].r-node[fi].l+1;//本区间长度
int len_r=len/2; //右子区间长度
int len_l=len-len_r; //左子区间长度
if(a[mid]<a[mid+1]) //区间合并产生新区间
{
node[fi].s=max(node[fi].s, node[fi*2].rs+node[fi*2+1].ls);
if(node[fi].ls==len_l)
node[fi].ls+=node[fi*2+1].ls;
if(node[fi].rs==len_r)
node[fi].rs+=node[fi*2].rs;
}
gx(fi);
}
//len上个区间的最大最长连续上升区间
//len_r上个区间以右端为结尾的最长连续上升区间
int len, len_r;
void cx(int i, int l, int r)//查询:模拟区间合并操作
{
if(l==node[i].l&&r==node[i].r)
{
len=max(len, node[i].s);
if(len_r!=0&&a[l-1]<a[l])
{
len=max(len, node[i].ls+len_r);
if(node[i].rs==node[i].r-node[i].l+1)
{
len_r=node[i].rs+len_r;
}
else
{
len_r=node[i].rs;
}
}
else
{
len_r=node[i].rs;
}
return;
}
int mid=(node[i].l+node[i].r)/2;
if(r<=mid)
cx(i*2, l, r);
else if(l>mid)
cx(i*2+1, l, r);
else
{
cx(i*2, l, mid);
cx(i*2+1, mid+1, r);
}
}
int main()
{
int n, m;
scanf("%d %d",&n, &m);
js(1, 1, n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
gx(f[i]);
}
len=0, len_r=0;
cx(1, 1, n);
cout<<len<<endl;
while(m--)
{
len=0, len_r=0;
int x, y;
scanf("%d%d",&x,&y);
a[x]=y;
gx(f[x]);
cx(1, 1, n);
cout<<len<<endl;
}
return 0;
}