线段树——区间合并
hdu3308
题意
更新单个点,对于询问的区间,求最长连续子序列的长度。
分析
区间合并模板题。
分析见代码。
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#define lson l, m, rt * 2
#define rson m + 1, r, rt * 2 + 1
using namespace std;
const int MAXN = 1e5 + 10;
int num[MAXN];
// sum 当前节点所处区间内的最长连续上升子序列长度
// lsum 当前节点所处区间从左端第一个点开始最长连续上升子序列长度
// rsum 当前节点所处区间以右端点为结尾的最长连续上升子序列长度
int sum[MAXN * 4], lsum[MAXN * 4], rsum[MAXN * 4];
void pushup(int l, int r, int rt)
{
lsum[rt] = lsum[rt * 2];
rsum[rt] = rsum[rt * 2 + 1];
sum[rt] = max(sum[rt * 2], sum[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]; // lsum[rt] = lsum[rt * 2]区间为递增区间,所以可以和右边的部分区间可以合并
if(rsum[rt] == len / 2) rsum[rt] += rsum[rt * 2]; // 同理
sum[rt] = max(sum[rt], rsum[rt * 2] + lsum[rt * 2 + 1]); // 这个两个区间是以 m 为割点分割的,合并区间
}
}
void build(int l, int r, int rt)
{
if(l == r)
{
sum[rt] = lsum[rt] = rsum[rt] = 1;
return;
}
int m = (l + r) / 2;
build(lson);
build(rson);
pushup(l, r, rt);
}
void update(int p, int l, int r, int rt)
{
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 sum[rt];
int m = (l + r) / 2;
if(m >= R) return query(L, R, lson);
if(m < L) return query(L, R, rson);
int a = query(L, R, lson);
int b = query(L, R, rson);
int ans = max(a, b);
if(num[m] < num[m + 1]) // 此时 L <= m < R ,保证答案一定在[L, R]区间内
{
int c = min(m - L + 1, rsum[rt * 2]) + min(R - m, lsum[rt * 2 + 1]);
ans = max(ans, c);
}
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 c[2]; int A, B;
scanf("%s%d%d", c, &A, &B); A++;
if(c[0] == 'U')
{
num[A] = B;
update(A, 1, n, 1);
}
else
{
printf("%d\n", query(A, B + 1, 1, n, 1));
}
}
}
return 0;
}
I. Candies
题意
给定一个只由A、B组成的序列,区间更新操作将选定区间的所有值变成指定的值,区间查询操作查询区间内最大连续B的数量。
分析
区间更新,区间合并问题。
加入延迟标记,和区间求和较类似。
本题可以用01序列代替AB序列,
sum 数组内存的就是最大有多少个连续的 1 的个数了,
suml 数组表示从当前节点区间左端点开始的最大连续 1 的个数,
sumr 数组表示以当前节点区间右端点结束的最大连续 1 的个数,
当区间赋值为 A 时,把在那个区间内的节点 的 sum 数组置为 0 即可。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lson l, m, rt * 2
#define rson m + 1, r, rt * 2 + 1
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int INF = 1e9;
const int MAXN = 1e6 + 10;
char s[MAXN];
int sum[MAXN * 4], suml[MAXN * 4], sumr[MAXN * 4], lazy[MAXN * 4];
void pushup(int l, int r, int rt)
{
suml[rt] = suml[rt * 2];
sumr[rt] = sumr[rt * 2 + 1];
sum[rt] = max(sum[rt * 2], sum[rt * 2 + 1]);
int m = (l + r) / 2;
int len = r - l + 1;
if(suml[rt] == len - len / 2) suml[rt] += suml[rt * 2 + 1];
if(sumr[rt] == len / 2) sumr[rt] += sumr[rt * 2];
sum[rt] = max(sum[rt], sumr[rt * 2] + suml[rt * 2 + 1]);
}
void pushdown(int l, int r, int rt)
{
int len = r - l + 1;
if(lazy[rt] == 2)
{
sum[rt * 2] = suml[rt * 2] = sumr[rt * 2] = len - len / 2;
sum[rt * 2 + 1] = suml[rt * 2 + 1] = sumr[rt * 2 + 1] = len / 2;
lazy[rt * 2] = lazy[rt * 2 + 1] = lazy[rt];
}
else if(lazy[rt])
{
sum[rt * 2] = suml[rt * 2] = sumr[rt * 2] = 0;
sum[rt * 2 + 1] = suml[rt * 2 + 1] = sumr[rt * 2 + 1] = 0;
lazy[rt * 2] = lazy[rt * 2 + 1] = lazy[rt];
}
lazy[rt] = 0;
}
void build(int l, int r, int rt)
{
lazy[rt] = 0;
if(l == r)
{
sum[rt] = suml[rt] = sumr[rt] = (s[l] == 'B');
return;
}
int m = (l + r) / 2;
build(lson);
build(rson);
pushup(l, r, rt);
}
void update(int L, int R, int p, int l, int r, int rt)
{
if(L <= l && r <= R)
{
lazy[rt] = p;
suml[rt] = sumr[rt] = sum[rt] = (p - 1) * (r - l + 1);
return;
}
pushdown(l, r, rt);
int m = (l + r) / 2;
if(m >= L) update(L, R, p, lson);
if(m < R) update(L, R, p, rson);
pushup(l, r, rt);
}
int query(int L, int R, int l, int r, int rt)
{
if(L <= l && r <= R) return sum[rt];
pushdown(l, r, rt);
int m = (l + r) / 2;
if(m >= R) return query(L, R, lson);
if(m < L) return query(L, R, rson);
int ans = max(query(L, R, lson), query(L, R, rson));
int c = min(m - L + 1, sumr[2 * rt]) + min(R - m, suml[rt * 2 + 1]);
ans = max(ans, c);
return ans;
}
int main()
{
int T, c = 1;
scanf("%d", &T);
while(T--)
{
int n, m;
scanf("%d%d", &n, &m);
scanf("%s", s + 1);
build(1, n, 1);
printf("Case #%d:\n", c++);
while(m--)
{
int c;
scanf("%d", &c);
int x, y, z;
if(c == 1)
{
scanf("%d%d%d", &x, &y, &z);
update(x, y, z, 1, n, 1);
}
else
{
scanf("%d%d", &x, &y);
printf("%d\n", query(x, y, 1, n, 1));
}
}
}
return 0;
}