线段树练习4

线段树练习4
codevs题号: 4919

时间限制: 1 s
空间限制: 128000 KB

题目描述 Description:

给你N个数,有两种操作
1:给区间[a,b]内的所有数都增加X
2:询问区间[a,b]能被7整除的个数

输入描述 Input Description

第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,表示操作的个数. 接下来Q行每行若干个整数。如果第一个数是add,后接3个正整数a,b,X,表示在区间[a,b]内每个数增加X,如果是count,表示统计区间[a,b]能被7整除的个数
输出描述 Output Description

对于每个询问输出一行一个答案
样例输入 Sample Input

3
2 3 4
6
count 1 3
count 1 2
add 1 3 2
count 1 3
add 1 3 3
count 1 3

样例输出 Sample Output

0

0

0

1
数据范围及提示 Data Size & Hint

100%:1< N<= 100000,1< Q<= 100000

题解:
比较水啊这题,我们可以在线段树的结构体数组中加上一个数组g[i]表示这个区间中与7取余为i的数有多少个。每次更新时都更新这个数组的值就好了,具体看代码。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long int a[200010],add[800010];
int n,m,x,y,v,pd,num=0;
struct hh
{
    int f,l,r,ls,rs;//ls为左子节点,rs为右子节点 
    long long int date;
    int g[10];//g[i]保存 此区间中和7取余为i的数右多少个 
}t[800010];
char pdd[10];
void build(int l,int r)
{

    num++;
    int i=num;
for(int j=0;j<7;j++)
t[i].g[j]=0;//手动初始化 
    t[i].l=l;t[i].r=r;
    if(l!=r)
    {
        t[i].ls=num+1;
        build(l,(l+r)/2);//建立左子节点 

        t[i].rs=num+1;
        build(((l+r)/2+1),r);//建立右子节点 
        for(int j=0;j<7;j++)//父节点的g数组的值就是子节点的g数组的和 
        {
            t[i].g[j]=t[t[i].ls].g[j]+t[t[i].rs].g[j];
        }

    }   
    else if(l==r)
    {
        t[i].g[(a[l]%7)]=1;//叶子节点 直接赋值 
    }

}
void pushdown(int i)//更新子节点的值 
{
    if(add[i]!=0)
    {
        int s[10],va=add[i];//利用一个s数组,保存更新后的值 
        for(int j=0;j<7;j++)
        {
            s[(j+va)%7]=t[t[i].ls].g[j];//左儿子更新数据 
        }
            for(int j=0;j<7;j++)//再把更新后数据赋给节点的g数组 
        t[t[i].ls].g[j]=s[j];


            for(int j=0;j<7;j++)
        {
            s[(j+va)%7]=t[t[i].rs].g[j];//右儿子更新数据 
        }
            for(int j=0;j<7;j++)//把更新后数据赋给节点的g数组 
        t[t[i].rs].g[j]=s[j];

        add[t[i].ls]+=va;
        add[t[i].rs]+=va;
        add[i]=0;
    }
}
void update(int i,int l,int r,int value)
{
    if(t[i].l==l&&t[i].r==r)//完全覆盖,直接算 
    {
        add[i]+=value;
        int s[10];
        for(int j=0;j<7;j++)
        {
            s[(j+value)%7]=t[i].g[j];//利用一个s数组,保存更新后的值 
        }
        for(int j=0;j<7;j++)//再把更新后数据赋给节点的g数组 
        t[i].g[j]=s[j];
        return ;
    }
    if(t[i].l==t[i].r) return ;
    pushdown(i);//把延迟更新的值传给子节点 
    int z=(t[i].l+t[i].r)/2;//区间更新的套路啦 
    if(r<=z) update(t[i].ls,l,r,value);
    else if(l>z) update(t[i].rs,l,r,value);
    else 
    {
        update(t[i].ls,l,z,value);
        update(t[i].rs,z+1,r,value);
    }
    for(int j=0;j<7;j++)//父节点的g数组的值就是子节点的g数组的和 
    {
        t[i].g[j]=t[t[i].ls].g[j]+t[t[i].rs].g[j];
    }
}
long long int query(int i,int l,int r)//求和 
{
    if(t[i].l==l&&t[i].r==r)//完全覆盖,直接返回值 
    {
        return t[i].g[0];
    }
    pushdown(i);//降延迟更新的值传给儿子节点 
    int z=(t[i].l+t[i].r)/2;//还是线段树区间更新的套路 
    if(r<=z) return query(t[i].ls,l,r);
    else if(l>z) return query(t[i].rs,l,r);
    else return query(t[i].ls,l,z)+query(t[i].rs,z+1,r);
}
int main()
{
    memset(add,0,sizeof(add));
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        a[i]%=7; //预处理先 mod7 
    }
    build(1,n);
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        scanf("%s",pdd);
        {
        if(pdd[0]=='a')
        {
            scanf("%d%d%d",&x,&y,&v);
            if((v%=7)!=0)//要加上的值如果和mod7为0的话就没什么用了,不必更新。 
            update(1,x,y,v%7);
        }   
        else if(pdd[0]=='c')
        {
            scanf("%d%d",&x,&y);
            cout<<query(1,x,y)<<endl;
        }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值