Gorgeous Sequence(线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5306
Problem Description
There is a sequence a of length n. We use ai to denote the i-th element in this sequence. You should do the following three types of operations to this sequence.

0 x y t: For every x≤i≤y, we use min(ai,t) to replace the original ai’s value.
1 x y: Print the maximum value of ai that x≤i≤y.
2 x y: Print the sum of ai that x≤i≤y.

Input
The first line of the input is a single integer T, indicating the number of testcases.

The first line contains two integers n and m denoting the length of the sequence and the number of operations.

The second line contains n separated integers a1,…,an (∀1≤i≤n,0≤ai<231).

Each of the following m lines represents one operation (1≤x≤y≤n,0≤t<231).

It is guaranteed that T=100, ∑n≤1000000, ∑m≤1000000.

Output
For every operation of type 1 or 2, print one line containing the answer to the corresponding query.

Sample Input

1
5 5
1 2 3 4 5
1 1 5
2 1 5
0 3 5 3
1 1 5
2 1 5

Sample Output

5
15
3
12

分析:
tle到wa有tle 又wa。终于ac。
55555555
不知道被运算过程中爆int,折腾了多少次了。在已经要把这个题存档的时候,突然看到大佬程序中乘了1LL。遂改,秒过。留下了菜鸡的泪水。

参考博客:https://blog.csdn.net/baidu_36797646/article/details/82853316
https://www.cnblogs.com/shenben/p/6641984.html
https://blog.csdn.net/nbl97/article/details/76696784
个人觉得这三篇博客的可读性还是比较高的。

同时建议:看一下:2016年国家队论文 吉如一 区间最值操作与历史最值问题

这个题目,有两个查询操作,一个改值操作。我们考虑如何用线段树来维护最大值,和。
对于一个序列来说,我们维护和 和最大值并不是很难的操作。重点便是如何改值。
在这里,我们同时维护最大值,次大值,最大值的个数。
对于一个t而言,如果他大于当前结点的最大值了,那么必然直接return。
如果t大于次大值,那么很明显这个结点里面的最大值都变成了t。同时可利用最大值个数修改sum。
如果t小于此大值,则要递归两个孩子。

思路很简单,代码还是很多,bug还是找不到。留下了菜鸡的泪水。

#include"stdio.h"
#include"string.h"
#include"algorithm"
using namespace std;
typedef long long ll;
const int N = 1000010;

ll sum[N << 2];
int max_val[N << 2],seg_val[N << 2],num[N << 2];
int T,a[N],n,m;

void Tap_down(int id,int rmax)
{
    if(max_val[id] <= rmax) return ;
    //这里注意,我一开始把rmax和max_val[id]写反了,好菜呀
    sum[id] = sum[id] - (max_val[id] - rmax) * (ll)num[id];
    max_val[id] = rmax;
}
void Push_down(int id)
{
   Tap_down(id << 1,max_val[id]);
   Tap_down(id << 1 | 1,max_val[id]);
}

void Push_up(int id)
{
    max_val[id] = max(max_val[id << 1],max_val[id << 1 | 1]);
    sum[id] = sum[id << 1] + sum[id << 1 | 1];
    if(max_val[id << 1] == max_val[id << 1 | 1])
    {
        seg_val[id] = max(seg_val[id << 1],seg_val[id << 1 | 1]);
        num[id] = num[id << 1] + num[id << 1 | 1]; return ;
    }
    if(max_val[id] == max_val[id << 1])
    {
        seg_val[id] = max(seg_val[id << 1],max_val[id << 1 | 1]); num[id] = num[id << 1]; return ;
    }
    seg_val[id] = max(seg_val[id << 1 | 1],max_val[id << 1]); num[id] = num[id << 1 | 1];
}

void Build_Tree(int id,int l,int r)
{
    max_val[id] = -1; seg_val[id] = -1;
    num[id] = 0; sum[id] = 0;
    if(l == r)
    {
        max_val[id] = a[l]; sum[id] = a[l]; num[id] = 1;
        return ;
    }
    int mid = (l + r) >> 1;
    Build_Tree(id << 1,l,mid);Build_Tree(id << 1 | 1,mid + 1,r);
    Push_up(id);
}

void Update(int id,int L,int R,int l,int r,int v)
{
    if(v >= max_val[id]) return ;
    if(l <= L && r >= R && seg_val[id] < v)
    {
        sum[id] = sum[id] - (max_val[id] - v) * (ll)num[id];
        max_val[id] = v;
        return ;
    }
    Push_down(id);
    int mid = (L + R) >> 1;
    if(l <= mid) Update(id << 1,L,mid,l,r,v);
    if(r > mid) Update(id << 1 | 1,mid + 1,R,l,r,v);
    Push_up(id);
}

ll Query_sum(int id,int L,int R,int l,int r)
{
    if(l <= L && r >= R) return sum[id];
    Push_down(id);
    int mid = (L + R) >> 1;
    ll ans = 0;
    if(l <= mid) ans += Query_sum(id << 1,L,mid,l,r);
    if(r > mid) ans += Query_sum(id << 1 | 1,mid + 1,R,l,r);
    return ans;
}

int Query_max(int id,int L,int R,int l,int r)
{
    if(l <= L && r >= R) return max_val[id];
    int mid = (L + R) >> 1; Push_down(id);
    int ans = 0;
    if(l <= mid) ans = max(ans,Query_max(id << 1,L,mid,l,r));
    if(r > mid) ans = max(ans,Query_max(id << 1 | 1,mid + 1,R,l,r));
    return ans;
}

void init()
{
    for(int i = 0; i <= n << 2; i ++)
        {
            sum[i] = num[i] = 0;
            max_val[i] = -1; seg_val[i] = -1;
        }
}

int main()
{
    scanf("%d",&T);
    while(T --)
    {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n; i ++)
            scanf("%d",&a[i]);
        //init();
        Build_Tree(1,1,n);
        while(m --)
        {
            int op,x,y; scanf("%d%d%d",&op,&x,&y);
            if(op == 1)
              printf("%d\n",Query_max(1,1,n,x,y));
            if(op == 2)
              printf("%lld\n",Query_sum(1,1,n,x,y));
            if(op == 0)
            {
                int t; scanf("%d",&t);
                Update(1,1,n,x,y,t);
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值