线段树第二弹!SHU1973 鸡排销售查询系统(区间更新+Lazy)

Description

看着虫虫重写的铁路购票系统使用非常方便,xyiyy想要虫虫帮忙实现一个鸡排销售情况查询的系统,主要是针对XX路上销售情况的查询。已知在XX路上,从东往西共有n个住户,标号分别为1,2……n-1,n,初始时,所有住户购买的鸡排数都为0。现只要求实现两个非常简单的功能,就是更新销售信息和查询[L,R]区间内有几位住户购买的鸡排数为3的倍数。系统命令的表示如下:

1)0 L R,表示标号在[L,R]范围内的所有住户都购买了一块鸡排。

2)1 L R,询问标号在[L,R]范围内购买的鸡排数目为3的倍数的住户数。

现有已知有Q条该系统的操作记录,但虫虫很忙,xyiyy希望你能帮忙实现这个系统。

Input

第一行包含两个数字n,Q,表示住户的数目,1<=n,Q<=100000。

接下来Q行由三个数字组成,0,L,R或者1,L,R,分别表示两种操作,1<=L,R<=n。

Output

对于每一次的询问操作输出购买的鸡排数目为3的倍数的住户数

Sample Input

4 7
1 1 4
0 2 3
0 2 4
1 1 1
0 1 4
1 4 4
1 1 4

Sample Output

4
1
0
2

这道题的关键点在于第二种操作,也就是询问[l,r]间购买鸡排数为3的倍数的用户数。
第一次提交TLE了,原因在于我是遍历[l,r]间的购买鸡排数。
这道题是我校EC-Final选拔赛的一道题,也正因为我的队友在最后时间做出来了这道题,我们才侥幸可以参加EC-Final。
在我TLE了之后,我偷偷地爬上队友的账号,才恍然大悟!
数组里放数据应该是鸡排数为3的倍数的用户数,而不是鸡排数。

AC的代码如下:
#include<iostream>
#include<algorithm>
#include<cstdio>
int const Maxn=100000;
using namespace std;
struct tree
{
    int left,right,add,y0,y1,y2;
}t[Maxn<<2];   //  线段树的空间大概是数组空间的4倍
void Pushup(int i)                           //更新父节点的值
{
    t[i].y0=t[i<<1].y0+t[(i<<1)+1].y0;
    t[i].y1=t[i<<1].y1+t[(i<<1)+1].y1;
    t[i].y2=t[i<<1].y2+t[(i<<1)+1].y2;
}
void Build(int i,int l,int r)
{
    t[i].y0=0;
    t[i].y1=0;
    t[i].y2=0;
    t[i].left=l;
    t[i].right=r;
    t[i].add=0;
    if(l==r)                    //由题意0也是3的倍数
    {
       t[i].y0=1;
       return;
    }
    else
    {
       int mid=(l+r)/2;
       Build(i<<1,l,mid);
       Build((i<<1)+1,mid+1,r);
       Pushup(i);
    }
}
void Pushdown(int i)      //  向下更新两个子节点的值
{
    int n=t[i].add;
    if(n){
        n=n%3;
        while(n--){
            int tmp=i<<1;
            int t0=t[tmp].y0;
            int t1=t[tmp].y1;
            int t2=t[tmp].y2;
            t[tmp].y0=t2;
            t[tmp].y1=t0;
            t[tmp].y2=t1;
            t[tmp].add++;
            t0=t[tmp+1].y0;
            t1=t[tmp+1].y1;
            t2=t[tmp+1].y2;
            t[tmp+1].y0=t2;
            t[tmp+1].y1=t0;
            t[tmp+1].y2=t1;
            t[tmp+1].add++;
        }
    }
        t[i].add=0;
}
void Update(int i,int l,int r)    
{
    if(l==t[i].left&&r==t[i].right)  //  如果区间完全重合,暂时不更新子节点的值,先记录需要更新的值。
    {
        int t0=t[i].y0;
        int t1=t[i].y1;
        int t2=t[i].y2;
        t[i].y0=t2;
        t[i].y1=t0;
        t[i].y2=t1;
        t[i].add++;
        return;
    }
    if(t[i].add)
        Pushdown(i);    
    int tmp=i<<1;
    int mid=(t[i].left+t[i].right)>>1;
    if(r<=mid)    // 要求的左右边界全部在当前区间的左半部分
        Update(tmp,l,r);
    else if(l>mid)     //要求的左右边界全部在当前区间的右半部分
        Update(tmp+1,l,r);
    else                    //要求的左右边界在当前区间的左右两半部分都有涉及,要切分开来计算
    {
        Update(tmp,l,mid);
        Update(tmp+1,mid+1,r);
    }
    Pushup(i);
}
int Query(int i,int l,int r)      //  查询区间l到r
{
    if(t[i].left==l&&t[i].right==r)    
    {
        return t[i].y0;
    }
    if(t[i].add)
        Pushdown(i);
    int mid=(t[i].left+t[i].right)>>1;
    if(r<=mid)
        return Query(2*i,l,r);
    else if(l>mid)
        return Query(2*i+1,l,r);
    else
    {
        return Query(2*i,l,mid)+Query(2*i+1,mid+1,r);
    }
}
int main(void)
{
    int n,q,t,l,r;
    scanf("%d%d",&n,&q);
    Build(1,1,n);
    while(q--)
    {
        scanf("%d%d%d",&t,&l,&r);
        if(t==0)
            Update(1,l,r);
        else
        {
            printf("%d\n",Query(1,l,r));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值