【线段树】言的幸运数

言的幸运数

时间限制(普通/Java) : 5000 MS/ 10000 MS          运行内存限制 : 65536 KByte
总提交 : 48            测试通过 : 7 

描述

每个人都有幸运数,言的幸运数是3。身为一个ACMer,言想考考你们,让你们帮忙统计数组中有多少数能被3整除。言给出了n个数,下标从0到n-1,最开始每个数都为0。现在你有两个操作
1 i j 数组下标范围在i, j之间的都增加1;
2 i j 下标范围在i, j之间的能有多少被3整除

输入

第一行一个整数n(1<=n<=100000)和q(1<=q<=50000),表示有n个数q次操作;接下来q行每行对应一个操作'1 i j' 或者'2 i j'(0<=i,j<n),n 和q 都为0结束运行

输出

For each case, print the case number first.对每个询问输出结果,占一行。

样例输入

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

样例输出

Case 1:
2
3
0
2


这题还是挺经典的,典型的线段树,难点在于判断范围里面被3整除的树不能用for循环判断,铁定超时啊(哭泣……)

主要是发现一个规律,每次加1,被3除余一,余二,余0的数的数目恰好互换:

int temp=t[k][0];
t[k][0]=t[k][2];
t[k][2]=t[k][1];
t[k][1]=temp;

(t[k][0]代表线段树中下标为K的树中被3除余0的数的数目)

弄懂了这个就容易了,具体代码如下:

#include<stdio.h>
#include<iostream>
using namespace std;
#define ls k<<1
#define rs k<<1|1
const int MAX=100005;
int n,t[MAX<<2][3],flag[MAX<<2];
void push_up(int k)
{
    t[k][0]=t[ls][0]+t[rs][0];
    t[k][1]=t[ls][1]+t[rs][1];
    t[k][2]=t[ls][2]+t[rs][2];
}
void build(int l,int r,int k)
{
    flag[k]=0;
    int mid=(l+r)>>1;
    if(l==r)
    {
        t[k][0]=1;
        t[k][1]=t[k][2]=0;
        return ;
    }
    build(l,mid,ls);
    build(mid+1,r,rs);
    push_up(k);
}
void change(int k)
{
    int temp=t[k][0];
    t[k][0]=t[k][2];
    t[k][2]=t[k][1];
    t[k][1]=temp;
}
void update(int l,int r,int L,int R,int k)
{
  //  cout<<":"<<L<<" "<<R<<" "<<k<<endl;
    if(l<=L&&r>=R)
    {
        flag[k]++;
        change(k);
        return ;
    }
    int mid=(L+R)>>1;
    if(flag[k])
    {
        flag[ls]+=flag[k];
        flag[rs]+=flag[k];
        int temp=flag[k]%3;
        while(temp--)
        {
            change(ls);
            change(rs);
        }
        flag[k]=0;
    }
    if(l<=mid)
    update(l,r,L,mid,ls);
    if(r>=mid+1)
    update(l,r,mid+1,R,rs);
    push_up(k);
}
int query(int l,int r,int L,int R,int k)
{
    int mid=(L+R)>>1;
    if(l<=L&&r>=R)
    {
       // cout<<"haha\n";
        return t[k][0];
    }
    if(flag[k])
    {
        flag[ls]+=flag[k];
        flag[rs]+=flag[k];
        int temp=flag[k]%3;
        while(temp--)
        {
            change(ls);
            change(rs);
        }
        flag[k]=0;
    }
    int ans=0;
    if(l<=mid)
        ans+=query(l,r,L,mid,ls);
    if(r>=mid+1)
        ans+=query(l,r,mid+1,R,rs);
    return ans;
}
int main()
{
    int q,i,j,x,y,z,p=0;
    while(~scanf("%d%d",&n,&q)&&n+q)
    {
        printf("Case %d:\n",++p);
        build(1,n,1);
        for(i=0;i<q;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            if(y>z)
                swap(y,z);
            if(x==1)
                update(y+1,z+1,1,n,1);
            else
                cout<<query(y+1,z+1,1,n,1)<<endl;
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值