线段树练习4

前方预警:
题目:http://codevs.cn/problem/4919/

这一题比起其前面难度应该是提升了不少,因为对于一段区间的一部分改变后我们无法确定被7整除的个数,而且在区间加中被7整除的个数也难以更新。

关于这样的题我们要学会转化,已知题目要求求被7整除的数,而被7整除的数每7次便会出现一次,那么我们就取一下%,存在数组中就可以了。对于一段区间被七整除的个数,在指针中有很好的处理方法,代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 5;
ll n,q,a,b,c,cnt = 0;
string s;
struct zt
{
    ll l,r,cnt[7],sz,jia;
    zt *lift,*right,*fa;
};
zt tree[MAXN];
void build(zt *root,ll l,ll r,zt *fa)
{
    root->l = l;root->r = r;root->sz = (r - l + 1);root->jia = 0;root->fa = fa;
    root->cnt[0] = root->sz;root->cnt[1] = 0;root->cnt[2] = 0;root->cnt[3] = 0;
    root->cnt[4] = 0;root->cnt[5] = 0;root->cnt[6] = 0;
    if(l == r)
        return;
    root->lift = &tree[++cnt];
    root->right = &tree[++cnt];
    ll mid = (l + r)>>1;
    build(root->right,mid + 1,r,root);
    build(root->lift,l,mid,root);
}
void add(zt *root,ll l,ll r,ll ql,ll qr,ll x)
{
    ll ji[7];
    if(root->jia&&l != r)
    {
        ll ji[7],ji2[7];
        for(ll i = 0;i < 7;i ++)
        {
            ji[(i + root->jia)%7] = root->lift->cnt[i];
            ji2[(i + root->jia)%7] = root->right->cnt[i];
        }
        for(int i = 0;i < 7;i ++)
        {
            root->lift->cnt[i] = ji[i];
            root->right->cnt[i] = ji2[i];
        }
        root->lift->jia += root->jia;
        root->right->jia += root->jia;  
        root->jia = 0;
    }
    if(l >= ql&&qr >= r)
    {
        for(int i = 0;i < 7;i ++)
            ji[(i + x)%7] = root->cnt[i];
        zt *j = root->fa;
        while(j != NULL)
        {
            for(int i = 0;i < 7;i ++)
            {
                j->cnt[i] -= root->cnt[i];
                j->cnt[i] += ji[i];
            }
            j = j->fa;
        }
        for(int i = 0;i < 7;i ++)
            root->cnt[i] = ji[i];
        root->jia += x;
        return;
    }
    ll mid = (l + r)>>1;
    if(ql <= mid)
        add(root->lift,l,mid,ql,qr,x);
    if(qr > mid)
        add(root->right,mid + 1,r,ql,qr,x);
}
ll find(zt *root,ll l,ll r,ll ql,ll qr)
{
    if(l >= ql&&qr >= r)
        return root->cnt[0];
    if(root->jia)
    {
        ll ji[7],ji2[7];
        for(ll i = 0;i < 7;i ++)
        {
            ji[(i + root->jia)%7] = root->lift->cnt[i];
            ji2[(i + root->jia)%7] = root->right->cnt[i];
        }
        for(int i = 0;i < 7;i ++)
        {
            root->lift->cnt[i] = ji[i];
            root->right->cnt[i] = ji2[i];
        }
        root->lift->jia += root->jia;
        root->right->jia += root->jia;  
        root->jia = 0;
    }
    ll mid = (l + r)>>1;
    if(ql > mid)
        return find(root->right,mid + 1,r,ql,qr);
    if(qr <= mid)
        return find(root->lift,l,mid,ql,qr);
    return (find(root->right,mid + 1,r,ql,qr) + find(root->lift,l,mid,ql,qr));

}
int main()
{
    scanf("%lld",&n);
    build(tree,1,n,NULL);
    for(int i = 1;i <= n;i ++)
    {
        scanf("%lld",&a);
        add(tree,1,n,i,i,a);
    }
    scanf("%lld",&q);
    for(int i = 1;i <= q;i ++)
    {
        cin>>s;
        if(s == "add")
        {
            scanf("%lld %lld %lld",&a,&b,&c);
            add(tree,1,n,a,b,c);
        }
        else
        {
            scanf("%lld %lld",&a,&b);
            cout<<find(tree,1,n,a,b)<<'\n';
        }
    } 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值