题目:造梦小孩
众所周知,九峰喜欢玩造梦西游。
孙悟空是造梦西游工作室的宠儿,从七魔王到上古天帝篇最强的角色都是孙悟空,九峰在用悟空通关了造梦西游1-5后决定重新玩一遍唐僧,希望能磨练自己的技术,而不是啥boss都无脑火魔斩就行了。于是他对唐僧的技能做了一些研究:
唐僧的攻击技能只有三种:
1.冰龙波,这是个废物技能,我们不会去用它
2.水魔爆,进行对点打击,破坏力巨大
3.玄冰阵,以人物为中心进行范围伤害,在左右一定距离处从地面向上穿出两颗冰刺,然后能量传递到两倍距离远处穿出同样的冰刺,三倍,四倍,以此类推无限扩张
九峰决定试一试除了冰龙波之外的技能,于是进入了VIP练习关卡,该关每个单位长度上都有一个无限血量的小怪,九峰会在图上乱放技能,但他有时候又想统计某个范围内造成的伤害总量,你能帮他算一算吗?
输入描述:
第一行输入两个正整数,表示地图长度和询问次数
接下来m行每行3或4个整数,格式如下:
1 x y: 表示九峰对x位置使用水魔爆并造成了y点伤害
2 x y len: 表示九峰在x位置使用了玄冰阵,对每个冰刺穿出的位置造成y点伤害,冰刺传递的距离为len
3 l r: 表示查询九峰对[l,r]区间的小怪一共造成了多少伤害
输出描述:
对于每个操作3,输出造成的伤害量
示例1
输入
复制
10 5
1 5 29
2 7 32 4
3 2 6
2 8 13 3
3 2 7
输出
复制
61
87
说明
三次技能对全图的伤害依次为:
[0,0,0,0,29,0,0,0,0,0]
[0,0,32,0,0,0,0,0,0,0]
[0,13,0,0,13,0,0,0,0,0]
分块题,操作1用树状数组维护,操作2当len<
n
\sqrt{n}
n时暴力做单点修改。
当len>=
n
\sqrt{n}
n时,用一个b[x][len]数组记录每次x开头长度为len的伤害,处理成前缀和,在查询时枚举len求和。
#include<bits/stdc++.h>
using namespace std;
namespace BIT{
typedef long long ll;
const int N=1e5;
const ll inf=0x3f3f3f3f3f3f3f3f;
int n;
ll c[N+5];
void init(int _n) {
n=_n;
for(int i=1;i<=n;i++) c[i]=0;
}
int lowbit(int x) {
return x&-x;
}
void modify(int x,int d) { //单点修改
for(int i=x;i<=n;i+=lowbit(i)) {
c[i]+=d;
}
}
ll query(int x) { //单点询问
ll s=0;
for(int i=x;i>0;i-=lowbit(i)) {
s=s+c[i];
}
return s;
}
}
using namespace BIT;
const int NN=4e2;
int nn;
ll b[NN+5][NN+5]; //b[x][len]
ll sum[NN+5][NN+5];
ll get(int x) {
ll s=0;
for(int i=1;i<=nn;i++) {
s+=(x/i)*sum[i][i]+sum[x%i][i];
}
return s;
}
int main() {
std::ios::sync_with_stdio(false);
int n,m;
cin>>n>>m;
init(n);
nn=sqrt(n);
while(m--) {
int opr;
cin>>opr;
if(opr==1) {
int x,y;
cin>>x>>y;
modify(x,y);
} else if(opr==2) {
int x,y,len;
cin>>x>>y>>len;
int st=x%len==0?len:x%len;
if(len>nn) {
for(int i=st;i<=n;i+=len) modify(i,y);
modify(x,-y);
} else {
b[st][len]+=y;
for(int i=1;i<=nn;i++) {
sum[i][len]=b[i][len]+sum[i-1][len];
}
modify(x,-y);
}
} else {
int l,r;
cin>>l>>r;
ll s=query(r)-query(l-1);
s+=get(r)-get(l-1);
cout<<s<<'\n';
}
}
return 0;
}