hdu - 4973 - A simple simulation problem.(线段树单点更新 + 区间更新)

题意:初始序列 1, 2, ..., n,m次操作(1 <= n,m<= 50000),每次操作可为:

D l r,将区间[l, r]中的所有数复制一次;

Q l r,输出区间[l, r]中同一数字个数的最大值。

(0 <= r – l <= 10^8, 1 <= l, r <= 序列元素个数)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4973

——>>因为区间内数字是依次递增的,所以可以以数字为叶建线段树去维护区间同一数字个数最大值。。

原查询区间[l, r],转换成区间[l位对应的数字,r位对应的数字]的线段树查询。。

接着就是SB的线段树操作了。。

注意区间的分段处理。。

#include <cstdio>
#include <algorithm>

#define lc (o << 1)
#define rc ((o << 1) | 1)

using std::max;

const int MAXN = 50000 + 10;

int mul[MAXN << 2];
long long sum[MAXN << 2], maxv[MAXN << 2];

int ReadInt()
{
    int ret = 0;
    char ch;

    while ((ch = getchar()) && ch >= '0' && ch <= '9')
    {
        ret = ret * 10 + ch - '0';
    }

    return ret;
}

void Pushdown(int o)
{
    if (mul[o])
    {
        mul[lc] += mul[o];
        mul[rc] += mul[o];
        sum[lc] <<= mul[o];
        sum[rc] <<= mul[o];
        maxv[lc] <<= mul[o];
        maxv[rc] <<= mul[o];
        mul[o] = 0;
    }
}

void Maintain(int o)
{
    sum[o] = sum[lc] + sum[rc];
    maxv[o] = max(maxv[lc], maxv[rc]);
}

void Build(int o, int L, int R)
{
    mul[o] = 0;
    if (L == R)
    {
        sum[o] = 1;
        maxv[o] = 1;
        return;
    }

    int M = (L + R) >> 1;
    Build(lc, L, M);
    Build(rc, M + 1, R);
    Maintain(o);
}

int QueryFigure(int o, int L, int R, long long index)
{
    if (L == R) return L;
    Pushdown(o);
    int M = (L + R) >> 1;
    return index <= sum[lc] ? QueryFigure(lc, L, M, index) : QueryFigure(rc, M + 1, R, index - sum[lc]);
}

long long QuerySum(int o, int L, int R, int ql, int qr)
{
    if (ql <= L && R <= qr) return sum[o];
    Pushdown(o);
    int M = (L + R) >> 1;
    long long ret = 0;
    if (ql <= M)
    {
        ret += QuerySum(lc, L, M, ql, qr);
    }
    if (qr > M)
    {
        ret += QuerySum(rc, M + 1, R, ql, qr);
    }

    return ret;
}

void UpdateInterval(int o, int L, int R, int ql, int qr)
{
    if (ql <= L && R <= qr)
    {
        sum[o] <<= 1;
        maxv[o] <<= 1;
        mul[o]++;
        return;
    }
    Pushdown(o);
    int M = (L + R) >> 1;
    if (ql <= M)
    {
        UpdateInterval(lc, L, M, ql, qr);
    }
    if (qr > M)
    {
        UpdateInterval(rc, M + 1, R, ql, qr);
    }
    Maintain(o);
}

void UpdateLeaf(int o, int L, int R, int q, int add)
{
    if (L == R)
    {
        sum[o] += add;
        maxv[o] += add;
        return;
    }
    Pushdown(o);
    int M = (L + R) >> 1;
    if (q <= M)
    {
        UpdateLeaf(lc, L, M, q, add);
    }
    else
    {
        UpdateLeaf(rc, M + 1, R, q, add);
    }
    Maintain(o);
}

void Update(int n, int l, int r, int ql, int qr)
{
    if (ql == qr)
    {
        UpdateLeaf(1, 1, n, ql, r - l + 1);
    }
    else
    {
        int addLeft = QuerySum(1, 1, n, 1, ql) - l + 1;
        int addRight = r - QuerySum(1, 1, n, 1, qr - 1);
        UpdateLeaf(1, 1, n, ql, addLeft);
        UpdateLeaf(1, 1, n, qr, addRight);
        if (qr - ql >= 2)
        {
            UpdateInterval(1, 1, n, ql + 1, qr - 1);
        }
    }
}

long long QueryMax(int o, int L, int R, int ql, int qr)
{
    if (ql <= L && R <= qr) return maxv[o];
    Pushdown(o);
    int M = (L + R) >> 1;
    long long ret = 0;
    if (ql <= M)
    {
        ret = max(ret, QueryMax(lc, L, M, ql, qr));
    }
    if (qr > M)
    {
        ret = max(ret, QueryMax(rc, M + 1, R, ql, qr));
    }

    return ret;
}

long long Query(int n, int l, int r, int ql, int qr)
{
    long long ret = 0;

    if (ql == qr)
    {
        ret = r - l + 1;
    }
    else
    {
        ret = max(ret, QuerySum(1, 1, n, 1, ql) - l + 1);
        ret = max(ret, r - QuerySum(1, 1, n, 1, qr - 1));
        if (qr - ql >= 2)
        {
            ret = max(ret, QueryMax(1, 1, n, ql + 1, qr - 1));
        }
    }

    return ret;
}

int main()
{
    int t, n, m, l, r, ql, qr, kase = 0;
    char op;

    scanf("%d", &t);
    getchar();
    while (t--)
    {
        scanf("%d%d", &n, &m);
        getchar();
        Build(1, 1, n);
        printf("Case #%d:\n", ++kase);
        while (m--)
        {
            op = getchar(); getchar();
            l = ReadInt();
            r = ReadInt();
            ql = QueryFigure(1, 1, n, l);
            qr = QueryFigure(1, 1, n, r);
            if (op == 'D')
            {
                Update(n, l, r, ql, qr);
            }
            else
            {
                printf("%I64d\n", Query(n, l, r, ql, qr));
            }
        }
    }

    return 0;
}
时间: 2024-10-02 02:29:37

hdu - 4973 - A simple simulation problem.(线段树单点更新 + 区间更新)的相关文章

hdu 4973 A simple simulation problem.(线段树)

http://acm.hdu.edu.cn/showproblem.php?pid=4973 有两种操作 D l r 将[l,r]区间翻倍 Q l r询问[l,r]中相同数字出现的最多次数 比赛的时候脑子太乱了,没有想到怎么做.发现每次翻倍序列的长度都在变化,区间对应的数也在变,没有思路. 但是静下心来想一想,思路还是挺清晰的. 无论怎么翻倍,序列中的数都是连续的,范围是1~n.可以拿一个数组来记录每个数出现的次数,当更新或询问区间[l,r]时,可以利用其前缀和找到区间[l,r]对应的数字分别是

HDOJ--4893--Wow! Such Sequence!【线段树+单点、区间更新】

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4893 题意:给你一个长度n的数列,初始都为0,有三种操作,第一种给第k个位置的数加d,第二种是查询区间 [l , r] 的总和,第三种是使区间 [l , r] 的值改为离它最近的那个斐波那契数的值. 我刚开始用sum数组存储节点的值,第三种操作是个区间更新,但是区间更新的值不一样,我就想当然的搜到最底部的节点来处理更新,果断T了.后来想了想,其实可以在节点上再加一个信息,就是这个值下次进行第三种操作要变

HDU4973A simple simulation problem.(线段树,区间更新)

A simple simulation problem. Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 330    Accepted Submission(s): 132 Problem Description There are n types of cells in the lab, numbered from 1 to n.

HDU-4937-A simple simulation problem.(线段树)

Problem Description There are n types of cells in the lab, numbered from 1 to n. These cells are put in a queue, the i-th cell belongs to type i. Each time I can use mitogen to double the cells in the interval [l, r]. For instance, the original queue

HDU4973:A simple simulation problem.(线段树)

Problem Description There are n types of cells in the lab, numbered from 1 to n. These cells are put in a queue, the i-th cell belongs to type i. Each time I can use mitogen to double the cells in the interval [l, r]. For instance, the original queue

【HDU】1754 I hate it ——线段树 单点更新 区间最值

I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 37448    Accepted Submission(s): 14816 Problem Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少.这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要

hdu 1698 Just a Hook(线段树之 成段更新)

Just a Hook                                                                             Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description In the game of DotA, Pudge's meat hook is actually the mos

HDU 1754 I Hate It (线段树 单点更新)

题目链接 中文题意,与上题类似. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 #include <cstdlib> 6 #include <algorithm> 7 const int maxn = 200000+10; 8 using namespace std; 9 int a[maxn], n, m; 10

HDU 1166 敌兵布阵 (线段树 单点更新)

题目链接 线段树掌握的很差,打算从头从最简单的开始刷一波, 嗯..就从这个题开始吧! 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 #include <cstdlib> 6 #include <algorithm> 7 const int maxn = 50000+10; 8 using namespace std