此题添加了更新条件,既对于区间[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;
}