HDU 4267 A Simple Problem with Integers (线段树)

本文介绍了一种使用线段树优化区间更新和模运算的问题解决方法,详细解释了如何通过维护特定模数下的节点来实现高效更新和查询操作。包括更新策略、查询方法以及代码实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

此题添加了更新条件,既对于区间[l,r],对于i属于[l,r],必须有(i-l)%k==0的点才能更新。由此知道i%k == l%k,因为只有这样才能满足(i-l)%k==0。考虑到k<=10,所以线段数的节点维护55个值。既res[rt][i]保存的是当前区间内除k余数为i。为什么要用55个值呢?因为需要避免重叠,比如res[rt][5],这个5不知道是模什么值得来的,k可以是6~10。这就导致了更新上重叠了。所以对于模k得到i,把i加上(1~k-1)就可以保证不会重叠了。比如k=1,余数为0,k=2,余数为0和1,但是加上k-1=1后,就是1,2,同理k=3时的余数0,1,2就变成3,4,5。所以k最大为10时需要维护的是55个数。所以例子中res[rt][5]其实表示的是该区间内模3余数为2的点。

更新的时候,只需把该区间内的res[rt][l%k]更新即可,原因是上述的i%k == l%k。res[rt][l%k]表示的是一个区间内和l同模的所以点。

查询操作要更新到底,注意从根节点开始记录更新情况,因为每个节点的res[][]值都不一样,都涵盖了具体某些点的更新情况。这跟常见的线段树更新有点不同,一般的线段树所维护的值都是有关联的。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls l,mid,rt<<1
#define rs mid+1,r,rt<<1|1
#define SIZE 50001

using namespace std;

int res[SIZE*3][55],arr[SIZE];
int N,Q;

void build(int l,int r,int rt)
{
    for(int i=0; i<55; i++)
        res[rt][i] = 0;
    if(l == r)return;
    int mid = (l + r) >> 1;
    build(ls);
    build(rs);
}

void update(int l,int r,int rt,int L,int R,int mod,int add)
{
    if(L <= l && r <= R)
    {
        int tem = L % mod;
        for(int i=1; i<mod; i++)
            tem += i;
        res[rt][tem] += add;
        return;
    }
    int mid = (l + r) >> 1;
    if(L <= mid)update(ls,L,R,mod,add);
    if(R > mid)update(rs,L,R,mod,add);
}

int query(int l,int r,int rt,int pos)
{
    int ret = 0,tem;
    for(int i=1; i<=10; i++)
    {
        tem = pos % i;
        for(int j=1; j<i; j++)
            tem += j;
        ret += res[rt][tem];
    }
    int mid = (l + r) >> 1;
    if(l == r)return ret;
    if(pos <= mid)ret += query(ls,pos);
    else ret += query(rs,pos);
    return ret;
}

int main()
{
    while(~scanf("%d",&N))
    {
        build(1,N,1);
        for(int i=1; i<=N; i++)
            scanf("%d",&arr[i]);
        scanf("%d",&Q);
        int op,s,e,mod,add,pos;
        while(Q--)
        {
            scanf("%d",&op);
            if(op == 1)
            {
                scanf("%d%d%d%d",&s,&e,&mod,&add);
                update(1,N,1,s,e,mod,add);
            }
            else
            {
                scanf("%d",&pos);
                printf("%d\n",arr[pos]+query(1,N,1,pos));
            }
        }
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值