[线段树实现区间最值]HDU5306 Gorgeous Sequence

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

Gorgeous Sequence

Problem Description

There is a sequence a a a of length n n n. We use a i a_i ai to denote the i i i-th element in this sequence. You should do the following three types of operations to this sequence.

0   x   y   t 0\ x\ y\ t 0 x y t: For every x ≤ i ≤ y x≤i≤y xiy, we use m i n ( a i , t ) min(a_i,t) min(ai,t) to replace the original a i a_i ai's value.
1   x   y 1\ x\ y 1 x y: Print the maximum value of a i a_i ai that x ≤ i ≤ y x≤i≤y xiy.
2   x   y 2\ x\ y 2 x y: Print the sum of a i a_i ai that x ≤ i ≤ y x≤i≤y xiy.

Input

The first line of the input is a single integer T T 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 a 1 , ⋯   , a n ( ∀ 1 ≤ i ≤ n , 0 ≤ a i < 2 31 ) a_1,\cdots,a_n(∀1≤i≤n,0≤a_i<2^{31}) a1,,an(1in,0ai<231).

Each of the following m lines represents one operation ( 1 ≤ x ≤ y ≤ n , 0 ≤ t < 2 31 ) (1≤x≤y≤n,0≤t<2^{31}) (1xyn,0t<231).

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

Output

For every operation of type 1 1 1 or 2 2 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

Hint

Please use efficient IO method

Author

XJZX

Source

2015 Multi-University Training Contest 2

Recommend

wange2014 | We have carefully selected several similar problems for you: 6937 6936 6935 6934 6933

题目大意

对长度为 n n n的数列 { a i } \{a_i\} {ai}进行 m m m次操作,共有三种类型的操作:

0   x   y   t 0\ x\ y\ t 0 x y t: 对所有的 a i , x ≤ i ≤ y a_i,x≤i≤y ai,xiy, 将 a i a_i ai变为 m i n ( a i , t ) min(a_i,t) min(ai,t)
1   x   y 1\ x\ y 1 x y: 输出区间 [ x , y ] [x,y] [x,y]中最大的 a i a_i ai的值。
2   x   y 2\ x\ y 2 x y: 输出 ∑ i = x y a i \sum_{i=x}^{y}a_i i=xyai

题解

除了维护区间和 s u m sum sum、最大值 m x mx mx这两个常规操作,为了使复杂度达到 l o g 2 n log_2n log2n级别,我们还需要维护每个区间的严格次大值 s e se se以及最大值的个数 c o t cot cot

t ≥ m x t\ge mx tmx时,操作无效直接返回;
s e < t < m x se<t< mx se<t<mx时,直接计算出区间和 s u m − = ( m x − t ) × v o t sum-=(mx-t)\times vot sum=(mxt)×vot,再更新最大值;
t ≥ s e t\ge se tse时,无法快速更新,直接向下递归更新再向上合并。

可以发现,整个算法的最大复杂度在于第三类情况。不过我们可以这样考虑:在整个长度为 n n n的数列 { a i } \{a_i\} {ai}里,不同的数至多有 n n n个,每当出现第三种情况时,至少最大值和次大值都会变成 t t t,即至少有两个数会变成相同的。而整棵线段树所有节点一共有 n l o g 2 n nlog_2n nlog2n个,所以最多这么多次操作后,整棵线段树的节点值的最大值都会变成一样的,严格次大值为 0 0 0。再加上其他操作的复杂度,可以得到整个算法的总复杂度不会超过 O ( ( n + m ) × l o g 2 n ) O((n+m)\times log_2n) O((n+m)×log2n)

据说可以证明复杂度是 O ( m l o g 2 n ) O(mlog_2n) O(mlog2n)的,但博主不会……

代码

退役老咸鱼含泪复习线段树……

以及这是第二次多组询问用 m e m s e t memset memset结果 T L E \mathcal{TLE} TLE了……

#include<bits/stdc++.h>
#define ls v<<1
#define rs v<<1|1
using namespace std;
const int M=1e6+5;
struct node{
    int le,ri,mx,se,cot,mn;
    long long sum;
}tree[M<<2];
int T,n,m,que[M];
int read()
{
    int r=0;char c=getchar();
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())r=(r<<3)+(r<<1)+c-'0';
    return r;
}
void up(int v)
{
    tree[v].sum=tree[ls].sum+tree[rs].sum;
    tree[v].mx=max(tree[ls].mx,tree[rs].mx);
    if(tree[ls].mx==tree[rs].mx)
    {
        tree[v].cot=tree[ls].cot+tree[rs].cot;
        tree[v].se=max(tree[ls].se,tree[rs].se);
    }
    else if(tree[ls].mx>tree[rs].mx)
    {
        tree[v].cot=tree[ls].cot;
        tree[v].se=max(tree[ls].se,tree[rs].mx);
    }
    else
    {
        tree[v].cot=tree[rs].cot;
        tree[v].se=max(tree[rs].se,tree[ls].mx);
    }
}
void push(int v,int mn)
{
    if(tree[v].mx<mn)return;
    tree[v].sum-=1ll*(tree[v].mx-mn)*tree[v].cot;
    tree[v].mx=tree[v].mn=mn;
}
void down(int v)
{
    if(tree[v].mn>=0)
    {
        push(ls,tree[v].mn);
        push(rs,tree[v].mn);
        tree[v].mn=-1;
    }
}
void build(int v,int le,int ri)
{
    tree[v].le=le,tree[v].ri=ri;
    tree[v].mn=-1;
    if(le==ri)
    {
        tree[v].cot=1;
        tree[v].se=0;
        tree[v].sum=tree[v].mx=que[le];
        return;
    }
    int mid=le+ri>>1;
    build(ls,le,mid),build(rs,mid+1,ri);
    up(v);
}
void qmin(int v,int le,int ri,int t)
{
    if(t>=tree[v].mx)return;
    if(le<=tree[v].le&&tree[v].ri<=ri&&tree[v].se<t)
    {
        tree[v].sum-=1ll*(tree[v].mx-t)*tree[v].cot;
        tree[v].mx=tree[v].mn=t;
        return;
    }
    down(v);
    int mid=tree[v].le+tree[v].ri>>1;
    if(le<=mid)qmin(ls,le,ri,t);
    if(mid<ri)qmin(rs,le,ri,t);
    up(v);
}
int askmax(int v,int le,int ri)
{
    if(le<=tree[v].le&&tree[v].ri<=ri)return tree[v].mx;
    int mid=tree[v].le+tree[v].ri>>1,re=0;
    down(v);
    if(le<=mid)re=askmax(ls,le,ri);
    if(mid<ri)re=max(re,askmax(rs,le,ri));
    return re;
}
long long asksum(int v,int le,int ri)
{
    if(le<=tree[v].le&&tree[v].ri<=ri)return tree[v].sum;
    int mid=tree[v].le+tree[v].ri>>1;
    long long re=0;
    down(v);
    if(le<=mid)re=asksum(ls,le,ri);
    if(mid<ri)re+=asksum(rs,le,ri);
    return re;
}
void in()
{
    //memset(tree,0,sizeof(tree));
    //scanf("%d%d",&n,&m);
    n=read(),m=read();
    for(int i=1;i<=n;++i)que[i]=read();//scanf("%d",&que[i]);
}
void ac()
{
    build(1,1,n);
    for(int i=1,op,x,y;i<=m;++i)
    {
        //scanf("%d%d%d",&op,&x,&y);
        op=read(),x=read(),y=read();
        if(!op)
        {
            //scanf("%d",&op);
            op=read();
            qmin(1,x,y,op);
        }
        else if(op-1)printf("%lld\n",asksum(1,x,y));
        else printf("%d\n",askmax(1,x,y));
    }
}
int main()
{
    scanf("%d",&T);
    for(;T;--T)in(),ac();
    //system("pause");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值