#6029. 「雅礼集训 2017 Day1」市场--线段树区间更新

题目链接:市场

题目:

1948: #6029. 「雅礼集训 2017 Day1」市场

Time Limit: 2 Sec   Memory Limit: 256 MB
Submit: 43   Solved: 8
[ Submit][ Status][ Web Board]

Description

从前有一个贸易市场,在一位执政官到来之前都是非常繁荣的,自从他来了之后,发布了一系列奇怪的政令,导致贸易市场的衰落。

n n n 个商贩,从0∼n−1 0 \sim n - 10n1 编号,每个商贩的商品有一个价格ai a_i ai,有两种政令:

  1. l,r,c l, r, c l,r,c,对于i∈[l,r],ai←ai+c i \in [l, r], a_i \leftarrow a_i + ci[l,r],aiai+c
  2. l,r,d l, r, d l,r,d,对于i∈[l,r],ai←⌊ai/d⌋ i \in [l, r], a_i \leftarrow \lfloor {a_i}/{d} \rfloori[l,r],aiai/d

现在有一个外乡的旅客想要了解贸易市场的信息,有两种询问方式:

  1. 给定 l,r l, r l,r,求mini∈[l,r]ai \min_{i \in [l, r]} a_imini[l,r]ai
  2. 给定 l,r l, r l,r,求∑i∈[l,r]ai \sum_{i\in [l, r]} a_i i[l,r]ai

Input

第一行为两个空格隔开的整数 n,q n, q n,q 分别表示商贩个数和政令 + 询问个数。
第二行包含 n n n 个由空格隔开的整数a0∼an−1 a_0 \sim a_{n - 1} a0an1
接下来 q q q 行,每行表示一个操作,第一个数表示操作编号1∼4 1 \sim 4 14,接下来的输入和问题描述一致。

Output

对于每个 3、4 操作,输出询问答案。

Sample Input

10 10
-5 -4 -3 -2 -1 0 1 2 3 4
1 0 4 1
1 5 9 1
2 0 9 3
3 0 9
4 0 9
3 0 1
4 2 3
3 4 5
4 6 7
3 8 9

Sample Output

-2
-2
-2
-2
0
1
1

HINT


对于 30% 30\% 30% 的数据,n,q≤10^{3} n, q \leq 10 ^ 3 n,q103

对于 60% 60\% 60% 的数据,保证数据随机;

对于 100% 100\% 100% 的数据,1≤n,q≤105,0≤l≤r≤n−1,c∈[−104,104],d∈[2,109] 1 \leq n, q \leq 10 ^ 5, 0 \leq l \leq r \leq n - 1, c \in [-10 ^ {4}, 10 ^ 4], d \in [2, 10 ^ 9]1n,q105,0lrn1,c[104,104],d[2,109]

    ......这道题写了一下午从5000ms超时写到2000ms超时......明明是区间更新一开始却写成单点更新然后进行一个区间循环更新......后来改成区间更新还是超时。写到死也没想到区间除法能当减法来写.........一下分析来自一位大佬  博客地址:jnxxhzz的博客

    【分析】

区间更新线段树...然后区间查询最小值min和区间和sum,只有一个点需要考虑就是那个区间做除法。
区间做除法所以可以考虑成区间做减法,那么对[l,r]这个区间做除法肯定不能对[l,r]这个区间做一次减法,因为需要减去的数肯定不同,但是可以考虑到一点就是如果某一段需要减去的数字是一样的,那就可以用区间更新的方法更新这一段,从而对整段[l,r]进行更新
所以对于某一段[l,r],判断这一段是否可以减去同一个数字的方法就是看这一段区间的最大值max和最小值min需要减去多少,如果max和min需要减去的数字是一样大的,那么说明这一段都可以减去这个数字,感觉题目中其实给了提示,因为题目中会询问区间最小值,那么既然已经要求最小值了,顺便求个最大值就可以了。
    贴自己模仿的代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 111111
#define LL long long
#define inf 0x3f3f3f
using namespace std;

LL sum[maxn*4],maxs[maxn*4],mins[maxn*4],lazy[maxn*4];
int num[maxn*4];

void Pushup(int t)
{
    sum[t]=sum[t*2]+sum[t*2+1];
    mins[t]=min(mins[t*2],mins[t*2+1]);
    maxs[t]=max(maxs[t*2],maxs[t*2+1]);
}

void Pushlazy(int t,int lz,int len)
{
    mins[t]+=lz;
    maxs[t]+=lz;
    sum[t]+=lz*len;
    lazy[t]+=lz;
}

void Pushdown(int l,int r ,int t)
{
    if(lazy[t]!=0)
    {
        int m=(l+r)/2;
        Pushlazy(t*2,lazy[t],m-l+1);
        Pushlazy(t*2+1,lazy[t],r-m);
        lazy[t]=0;
    }
    return;
}

void Build(int l,int r,int t)
{
    lazy[t]=0;
    if(l==r)
    {
        sum[t]=mins[t]=maxs[t]=num[l];
        return;
    }
    int m=(l+r)/2;
    Build(l,m,t*2);
    Build(m+1,r,t*2+1);
    Pushup(t);
}

void add(int x,int l,int r,int L,int R,int t)
{
    if(l>=L&&r<=R)
    {
        lazy[t]+=x;
        sum[t]+=x*(r-l+1);
        mins[t]+=x;
        maxs[t]+=x;
        return;
    }
    Pushdown(l,r,t);
    int m=(l+r)/2;
    if(L<=m)
        add(x,l,m,L,R,t*2);
    if(R>m)
        add(x,m+1,r,L,R,t*2+1);
    Pushup(t);
}

void div(int x,int l,int r,int L,int R,int t)
{
    if(l>=L&&r<=R)
    {
        LL A,B;
        if(mins[t]<0)
            A=(mins[t]-x+1)/x;
        else
            A=mins[t]/x;
        if(maxs[t]<0)
            B=(maxs[t]-x+1)/x;
        else
            B=maxs[t]/x;
        if(A-mins[t]==B-maxs[t])
        {
            Pushlazy(t,A-mins[t],r-l+1);
            return;
        }
    }
    Pushdown(l,r,t);
    int m=(l+r)/2;
    if(L<=m)
        div(x,l,m,L,R,t*2);
    if(R>m)
        div(x,m+1,r,L,R,t*2+1);
    Pushup(t);
}

LL Querysum(int L,int R,int l,int r,int t)
{
    if(l>=L&&r<=R)
        return sum[t];
    Pushdown(l,r,t);
    int m=(l+r)/2;
    LL ans=0;
    if(L<=m)
        ans+=Querysum(L,R,l,m,t*2);
    if(R>m)
        ans+=Querysum(L,R,m+1,r,t*2+1);
    return ans;
}
LL Querymin(int L,int R,int l,int r,int t)
{
    if(l>=L&&r<=R)
        return mins[t];
    Pushdown(l,r,t);
    int m=(l+r)/2;
    LL ans=inf;
    if(L<=m)
        ans=min(ans,Querymin(L,R,l,m,t*2));
    if(R>m)
        ans=min(ans,Querymin(L,R,m+1,r,t*2+1));
    return ans;
}

int main()
{
    int n,q,p;
    int a,b,c,d;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
         scanf("%d",&num[i]);
    Build(1,n,1);
    for(int i=1;i<=q;i++)
    {
        scanf("%d",&p);
        if(p==1)
        {
            scanf("%d%d%d",&a,&b,&c);
            add(c,1,n,a+1,b+1,1);
        }
        else if(p==2)
        {
            scanf("%d%d%d",&a,&b,&d);
            div(d,1,n,a+1,b+1,1);
        }
        else if(p==3)
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",Querymin(a+1,b+1,1,n,1));
        }
        else
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",Querysum(a+1,b+1,1,n,1));
        }
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值