HDU 5306 Gorgeous Sequence 吉司机线段树


题目链接

题目描述 Description

给一个序列
支持3种操作
0 u v t 对于所有i u<=i<=v a[i]=min(a[i],t)
1 u v 对于所有i u<=i<=v,输出最大的a[i]
2 u v 对于所有i u<=i<=v,输出a[i]的和

输入描述 Input Description

多组测试数据,第一行T表示数据组数
对于每组数据,一行n,m表示长度和操作个数
接下来一行n个数代表序列的值
接下来m行每行一个操作

输出描述 Output Description

对于每一个询问输出一行表示答案

水题分析 Waterproblem Analysis

根据题目的描述以及输入和输出基本上推断出应该是线段树的题目。一开始只记录区间的最大值与区间合,更新时每次都要递归到底,时间复杂度就变成了O(n^2logn)显然是不够的。
那么就要引入吉司机线段树。吉司机线段树的可以用来处理区间最值更新的问题,其主要思想是在维护最大值的同时记录次数和次小值。
当我们在更新区间最值时分成三种情况。
1.如果小于等于要求改的最值,就不需要做处理。
2.如果大于次小值,那么我们就可以根据其与最大值的差值和最大值的个数就可以O(1)的更新答案了,同时打上标记。
3.如果小于等于次小值,再向左右儿子递归。
有了这三条规则,这样才能保证lazy标记下放时直接更新子区间最大值和区间合的正确性,因为要更改的值大于次大值,所以不可能更小,与父亲区间更新的操作相同。
用势能分析的方法可以证明时间复杂度为O(mlogn)。

这道题卡常数,一定要注意代码书写的风格。c++自带的max,min函数跑的是真的很慢。

附上代码:

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<cstring>
#define ll long long
#define INF 0x3f3f3f3f
#define EF if(ch==EOF) return x;
using namespace std;

const int maxn=1e6+10;
int n,q,T;
ll cnt[maxn<<2];
ll a[maxn];
ll sum[maxn<<2];
ll maxx[maxn<<2],smaxx[maxn<<2],lazy[maxn<<2];

 void pushup(int d) {
    sum[d]=sum[d<<1]+sum[d<<1|1];
    maxx[d]=max(maxx[d<<1],maxx[d<<1|1]);
    smaxx[d]=max(smaxx[d<<1|1],smaxx[d<<1]);
    if(maxx[d<<1]!=maxx[d<<1|1]) smaxx[d]=max(smaxx[d],min(maxx[d<<1],maxx[d<<1|1]));
    cnt[d]=0;
    if(maxx[d]==maxx[d<<1]) {
        cnt[d]+=cnt[d<<1];
    }
    if(maxx[d]==maxx[d<<1|1]) {
        cnt[d]+=cnt[d<<1|1];
    }
}

 void pushdown(int d) {
    if(maxx[d<<1]>lazy[d]) {
        sum[d<<1]+=1ll*cnt[d<<1]*(lazy[d]-maxx[d<<1]);
        lazy[d<<1]=maxx[d<<1]=lazy[d];
    }
    if(maxx[d<<1|1]>lazy[d]) {
        sum[d<<1|1]+=1ll*cnt[d<<1|1]*(lazy[d]-maxx[d<<1|1]);
        lazy[d<<1|1]=maxx[d<<1|1]=lazy[d];
    }
    lazy[d]=-1;
}

void build(int d,int l,int r) {
    lazy[d]=-1;
    smaxx[d]=-1;
    if(l==r) {
        sum[d]=maxx[d]=a[l];
        cnt[d]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(d<<1,l,mid);
    build(d<<1|1,mid+1,r);
    pushup(d);
}

void modify(int d,int L,int R,int l,int r,ll x) {
    if(maxx[d]<=x) return;
    if(L==l&&R==r&&smaxx[d]<x) {
        sum[d]+=1ll*cnt[d]*(x-maxx[d]);
        lazy[d]=x;
        maxx[d]=x;
        return;
    }
    if(lazy[d]!=-1) pushdown(d);
    int mid=(L+R)>>1;
    if(r<=mid) modify(d<<1,L,mid,l,r,x);
    else if(l>mid) modify(d<<1|1,mid+1,R,l,r,x);
    else {
        modify(d<<1,L,mid,l,mid,x);
        modify(d<<1|1,mid+1,R,mid+1,r,x);
    }
    pushup(d);
}

ll query_max(int d,int L,int R,int l,int r) {
    if(L==l&&R==r) {
        return maxx[d];
    }
    if(lazy[d]!=-1) pushdown(d);
    int mid=(L+R)>>1;
    if(r<=mid) return query_max(d<<1,L,mid,l,r);
    else if(l>mid) return query_max(d<<1|1,mid+1,R,l,r);
    else return max(query_max(d<<1,L,mid,l,mid),query_max(d<<1|1,mid+1,R,mid+1,r));
}

ll query_sum(int d,int L,int R,int l,int r) {
    if(L==l&&R==r) {
        return sum[d];
    }
    if(lazy[d]!=-1) pushdown(d);
    int mid=(L+R)>>1;
    if(r<=mid) return query_sum(d<<1,L,mid,l,r);
    else if(l>mid) return query_sum(d<<1|1,mid+1,R,l,r);
    else return query_sum(d<<1,L,mid,l,mid)+query_sum(d<<1|1,mid+1,R,mid+1,r);
}

int main() {
    for(scanf("%d",&T);T--;) {
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
        build(1,1,n);
        for(int i=1;i<=q;++i) {
            int op,l,r;
            ll x;
            scanf("%d%d%d",&op,&l,&r);
            if(op==0) {
                scanf("%lld",&x);
                modify(1,1,n,l,r,x);
            }
            if(op==1) {
                printf("%lld\n",query_max(1,1,n,l,r));
            }
            if(op==2) {
                printf("%lld\n",query_sum(1,1,n,l,r));
            }
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值