分块之区间查询与区间修改

给出一个长为n的数列,以及n个操作,操作涉及区间加法,区间求和。

这题的询问变成了区间上的询问,不完整的块还是暴力;而要想快速统计完整块的答案,需要维护每个块的元素和,先要预处理一下。

考虑区间修改操作,不完整的块直接改,顺便更新块的元素和;完整的块类似之前标记的做法,直接根据块的元素和所加的值计算元素和的增量。

更改后的区间加法

 1 void interval_add(LL ll,LL rr,LL v)
 2 {
 3     for(LL i=ll;i<=min(where[ll]*m,rr);i++)
 4     //这里判断的是where[ll]是不完全块的情况,也就是ll在他实际块最左端的右侧,
 5     // 然后便利ll-所在块的结尾/rr,暴力增加
 6         a[i]+=v,sum[where[ll]]+=v;
 7         // 注意sum表示的是一个块内的元素和,where表示的是块的位置
 8     if(where[ll]!=where[rr])
 9     // 注意如果是ll和rr在一个块中的话,上面已经加过一边,所以不用加
10     {
11         for(LL i=(where[rr]-1)*m+1;i<=rr;i++)
12         // 这里判断的是rr在他实际所在块的最右端左侧的情况
13         // where[i]*m表示的是第i个块最右端的元素
14         // where[rr]-1就是rr所在块左边那个块最右端的元素
15         // 一直到rr暴力增加
16             a[i]+=v,sum[where[rr]]+=v;
17     }
18     for(LL i=where[ll]+1;i<=where[rr]-1;i++)
19     //这里where[ll]和where[rr]均已暴力处理过,所以只枚举中间的块就可以
20         add[i]+=v;
21 } 

区间查询

 1 void interval_ask(LL ll,LL rr)
 2 {
 3     LL ans=0;
 4     for(LL i=ll;i<=min(where[ll]*m,rr);i++)
 5         ans+=a[i]+add[where[i]];
 6         // 暴力求和 ,注意where里面要写ll\i,因为我们始终是在ll到它的所在区间结尾的元素内循环
 7         //
 8     if(where[ll]!=where[rr])
 9         for(LL i=(where[rr]-1)*m+1;i<=rr;i++)
10         //注意这里需要加一,因为所有的for都是<=,如果不写+1会加两边
11             ans+=a[i]+add[where[i]];
12
13     for(LL i=where[ll]+1;i<=where[rr]-1;i++)
14         ans+=sum[i]+add[i]*m;
15         // 这里要*区间内元素的个数
16
17     printf("%lld\n",ans);
18 }

完整代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #define LL long long
 6 using namespace std;
 7 const LL MAXN=100001;
 8 LL n,q,m,how,l,r,v;
 9 LL a[MAXN];// 初始值
10 LL add[MAXN];// 后来每个块上加入的值
11 LL where[MAXN];// 记录每一个值对应第几块
12 LL sum[MAXN];//记录每一块内的元素和
13 void interval_add(LL ll,LL rr,LL v)
14 {
15     for(LL i=ll;i<=min(where[ll]*m,rr);i++)
16     //这里判断的是where[ll]是不完全块的情况,也就是ll在他实际块最左端的右侧,
17     // 然后便利ll-所在块的结尾/rr,暴力增加
18         a[i]+=v,sum[where[ll]]+=v;
19         // 注意sum表示的是一个块内的元素和,where表示的是块的位置
20     if(where[ll]!=where[rr])
21     // 注意如果是ll和rr在一个块中的话,上面已经加过一边,所以不用加
22     {
23         for(LL i=(where[rr]-1)*m+1;i<=rr;i++)
24         // 这里判断的是rr在他实际所在块的最右端左侧的情况
25         // where[i]*m表示的是第i个块最右端的元素
26         // where[rr]-1就是rr所在块左边那个块最右端的元素
27         // 一直到rr暴力增加
28             a[i]+=v,sum[where[rr]]+=v;
29     }
30     for(LL i=where[ll]+1;i<=where[rr]-1;i++)
31     //这里where[ll]和where[rr]均已暴力处理过,所以只枚举中间的块就可以
32         add[i]+=v;
33 }
34 void interval_ask(LL ll,LL rr)
35 {
36     LL ans=0;
37     for(LL i=ll;i<=min(where[ll]*m,rr);i++)
38         ans+=a[i]+add[where[i]];
39         // 暴力求和 ,注意where里面要写ll,因为我们始终是在ll到它的所在区间结尾的元素内循环
40
41     if(where[ll]!=where[rr])
42         for(LL i=(where[rr]-1)*m+1;i<=rr;i++)
43         //注意这里需要加一,因为所有的for都是<=,如果不写+1会加两边
44             ans+=a[i]+add[where[i]];
45
46     for(LL i=where[ll]+1;i<=where[rr]-1;i++)
47         ans+=sum[i]+add[i]*m;
48
49     printf("%lld\n",ans);
50 }
51 int main()
52 {
53     scanf("%lld",&n);
54     scanf("%lld",&q);
55     m=sqrt(n);
56     for(LL i=1;i<=n;i++)
57         scanf("%lld",&a[i]);
58     for(LL i=1;i<=n;i++)
59         where[i]=(i-1)/m+1,sum[where[i]]+=a[i];// 这里的i可以-1(hzwer写的是-1)也可以不写,不写的话第一块的元素个数会是m-1
60
61     for(LL i=1;i<=q;i++)
62     {
63         scanf("%lld",&how);
64         if(how==1)// 区间加
65         {
66             scanf("%lld%lld%lld",&l,&r,&v);
67             interval_add(l,r,v);
68         }
69         else// 单点查询
70         {
71             scanf("%lld%lld",&l,&r);
72             interval_ask(l,r);
73             // where保存的是这个点所属的块,add表示这个块已经增加的元素
74             //a[v]是这个点开始的值,一加就是答案
75         }
76     }
77     return 0;
78 }
时间: 2024-10-07 08:20:42

分块之区间查询与区间修改的相关文章

区间查询,区间修改

1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 typedef long long ll; 7 8 const int N=2e5+5; 9 int n,m; 10 ll lazy[N<<2],sum[N<<2]; 11 12 void PushUp(int rt

树状数组的区间修改与单点查询与区间查询

如何将普通树状数组升级 普通的单点修改单点查询就不讲了,从区间修改和单点查询讲起. 原来的值存在a[]里面,多建立个数组c1[],注意:c1[i]=a[i]-a[i-1]. 那么求a[i]的值的时候a[i]=a[i-1]+c1[i]=a[i-2]+c1[i]+c1[i-1]=-..=c1[1]+c1[2]+-+c1[i]. 所以就用c1[]建立树状数组,便可以很快查询a[i]的值.不多说,见代码. #include<iostream> #include<cstdio> #defin

【LuoguP3038/[USACO11DEC]牧草种植Grass Planting】树链剖分+树状数组【树状数组的区间修改与区间查询】

模拟题,可以用树链剖分+线段树维护. 但是学了一个厉害的..树状数组的区间修改与区间查询.. 分割线里面的是转载的: -------------------------------------------------------------------------------- [ 3 ]  上面都不是重点--重点是树状数组的区间修改+区间查询 这个很好玩 其实也挺简单 首先依旧是引入delta数组 delta[i]表示区间 [i, n] 的共同增量 于是修改区间 [l, r] 时修改 delt

树状数组 区间修改+区间查询

其实直到不久前都几乎不会用树状数组,请教了PPZ大佬之后终于懂了一点点. 然后小蒟蒻现在才知道了树状数组区间修改+区间查询的方法QAQ 传送门 Codevs 线段树练习3 //Twenty #include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<queu

【bzoj3779】重组病毒 LCT+树上倍增+DFS序+树状数组区间修改区间查询

题目描述 给出一棵n个节点的树,每一个节点开始有一个互不相同的颜色,初始根节点为1. 定义一次感染为:将指定的一个节点到根的链上的所有节点染成一种新的颜色,代价为这条链上不同颜色的数目. 现有m次操作,每次为一下三种之一: RELEASE x:对x执行一次感染: RECENTER x:把根节点改为x,并对原来的根节点执行一次感染: REQUEST x:询问x子树中所有节点感染代价的平均值. 输入 输入的第一行包含两个整数n和m,分别代表局域网中计算机的数量,以及操作和询问的总数.接下来n-1行,

树状数组实现区间修改+区间查询

事实上,这只是我弱弱的luogu博客的存档-- 线段树模板(1) 题意要求:给定一个序列,支持区间修改和区间查询. 智障数据结构模板题-- 当然,题目名字告诉我们要用线段树.但是线段树很长,容易出现问题,而且跑得稍慢,所以就有dalao开始yy:可不可以让树状数组支持区间修改和查询呢? 于是伟大的"超级树状数组"横空出世了. 首先,我们看树状数组是如何支持区间修改的: 设 tree[i]=a[i]-a[i-1] (差分),那么容易得到: tree[1]+tree[2]+--+tree[

二维树状数组模板(区间修改+区间查询)

二维树状数组模板(区间修改+区间查询) 例题:JOIOI上帝造题的七分钟 一共两种操作: \(L\ x_1\ y_1\ x_2\ y_2\ d\):把\((x_1,y_1)\),\((x_2,y_2)\)这个矩形内所有元素加\(d\). \(k\ x_1\ y_1\ x_2\ y_2\):查询\((x_1,y_1)\),\((x_2,y_2)\)这个矩形内所有元素的和. 代码如下: #include<bits/stdc++.h> #define RG register #define IL i

【bzoj5173】[Jsoi2014]矩形并 扫描线+二维树状数组区间修改区间查询

题目描述 JYY有N个平面坐标系中的矩形.每一个矩形的底边都平行于X轴,侧边平行于Y轴.第i个矩形的左下角坐标为(Xi,Yi),底边长为Ai,侧边长为Bi.现在JYY打算从这N个矩形中,随机选出两个不同的矩形,并计算它们的并的大小.JYY想知道,交的大小的期望是多少.换句话说即求在所有可能的选择中,两个矩形交的面积的平均大小是多大. 输入 输入一行包含一个正整数N. 接下来N行,每行4个整数,分别为Xi,Yi,Ai,Bi 2 < =  N < =  2*10^5, 0 < =  Xi,

[线段树模板] 区间修改 区间查询(详注)

输入 每个测试点(输入文件)有且仅有一组测试数据. 每组测试数据的第1行为一个整数N,意义如前文所述. 每组测试数据的第2行为N个整数,分别描述每种商品的重量,其中第i个整数表示标号为i的商品的重量Pi. 每组测试数据的第3行为一个整数Q,表示小Hi进行的操作数. 每组测试数据的第N+4~N+Q+3行,每行分别描述一次操作,每行的开头均为一个属于0或1的数字,分别表示该行描述一个询问和一次商品的价格的更改两种情况.对于第N+i+3行,如果该行描述一个询问,则接下来为两个整数Li, Ri,表示小H