线段树——区间合并

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;
}
posted @ 2017-05-01 09:28  ftae  阅读(593)  评论(0编辑  收藏  举报