LCIS(HDOJ 3308)
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].
Input
T in the first line, indicating the case number.
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).
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
My Code
这题就是关键是求LCIS,即最长连续上升序列( longest consecutive increasing subsequence ),线段树可以实现这个操作,最重要的就是区间合并的部分,这也是这题最难处理的一部分.
#include
#include
#include
#define maxn 100000 + 5
using namespace std;
struct node
{
int l; //区间边界
int r;
int len;//区间长度
int lmax;//以l开始的LCIS长度
int rmax;//到r结束的LCIS长度
int max;//区间[l,r]中LCIS长度
} tree[maxn << 2]; //要开四倍大小
int a[maxn];
int t, n, m;
int cnt;
void pushup(int p) //此题关键函数
{
//这里就不解释了吧
tree[p].lmax = tree[p << 1].lmax;
tree[p].rmax = tree[p << 1 | 1].rmax;
tree[p].max = max(tree[p << 1].max, tree[p << 1 | 1].max);
int m = (tree[p].l + tree[p].r) >> 1;
//判断中间连接部分是否可以合并
if (a[m] < a[m + 1])
{
if (tree[p << 1].len == tree[p].lmax) //如果左区间整段为LCIS
tree[p].lmax += tree[p << 1 | 1].lmax;//那么合并后就需要更新lmax
if (tree[p << 1 | 1].len == tree[p].rmax)//同理右区间整段为LCIS的话也是一样
tree[p].rmax += tree[p << 1].rmax;
//合并后产生更长的CIS的话就更新
tree[p].max = max(tree[p].max, tree[p << 1].rmax + tree[p << 1 | 1].lmax);
}
// cout << tree[p].l << ' ' << tree[p].r << ' ' << tree[p].lmax << ' ' << tree[p].rmax << ' ' << tree[p].max << endl;
//调试用
}
void build(int l, int r, int p)
{
tree[p].l = l; //初始化
tree[p].r = r;
tree[p].len = r - l + 1;
if (l == r)
{
//只有一个点的话,LCIS长度肯定是1
tree[p].max = tree[p].lmax = tree[p].rmax = 1;
return;
}
int m = (l + r) >> 1;
build(l, m, p << 1);
build(m + 1, r, p << 1 | 1);
pushup(p);
}
void update(int x, int v, int p)
{
if (tree[p].l == tree[p].r)
{
a[x] = v;//这句放在主函数也行,可以让这个函数少一个形参
return;
}
int m = (tree[p].l + tree[p].r) >> 1;
if (x <= m)
update(x, v, p << 1);
else
update(x, v, p << 1 | 1);
pushup(p); //向上更新
}
int query(int l, int r, int p)
{
if (tree[p].r < l || r < tree[p].l)
return 0;
if (l <= tree[p].l && tree[p].r <= r)
return tree[p].max;
int m = (tree[p].l + tree[p].r) >> 1;
//这里lmax和rmax和tree里的意思不一样,这里分别代表左区间和右区间的LCIS长度
int lmax = query(l, r, p << 1);
int rmax = query(l, r, p << 1 | 1);
//如果查询区间是完整的话,可以直接返回
if (r <= m)
return lmax;
if (m < l)
return rmax;
//查询区间是被分隔开的话,还要再看看能不能合并
int ans = max(lmax, rmax);//不能合并的话就是左右区间的LCIS中更大的那个
if (a[m] < a[m + 1])//这里容易理解的吧
ans = max(ans, min(tree[p << 1].rmax, m - l + 1) + min(tree[p << 1 | 1].lmax, r - m));
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cout.tie(NULL);
cin >> t;
while (t--)
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
build(1, n, 1);
while (m--)
{
char c;
int x, y;
cin >> c >> x >> y;
if (c == 'U')
update(x + 1, y, 1);//数据的编号是从0开始的,线段树是从1开始的,所以要加1
else if (c == 'Q')
cout << query(x + 1, y + 1, 1) << endl;//同理编号加1
// else //调试用
// cout << "Wrong!!!" << endl;
}
}
return 0;
}