A Simple Problem with Integers POJ - 3468(线段树+区间查询+区间修改+建树+懒惰标记模板)+(树状数组)

题意:

有一个数组,有两种操作。1: Q a b 求[a,b]的和 2:C a b c 给[a,b] 的所有元素都加上c。

题目:

You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of AaAa+1, ... ,Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15

Hint

The sums may exceed the range of 32-bit integers.

题解

区间求和加区间更新,典型的带懒惰标记的线段树。懒惰标记:更新的时候 并不将每个元素都更新,而是将要更新的区间打上懒惰标记,等下次要使用的时候 再向下更新。

线段树的基本思想:二分,线段树是一棵二叉搜索树,它储存的是一个区间的信息

算法思想:

1 。线段树的成段更新--延迟更新;

2。区间查询和更新的时候加入一个延迟节点;

3 。每次要在下次查询或者更新到该区间时;

4 。再把节点的信息传递到左右孩子的结点上; 这样更新大大减少了时间和空间上的开销;

算法过程: 1 。每次更新并不需要更新到叶节点;

2 。只需更新到相应的段就可以了,然后记录个add(懒惰标记);

3 。下次更新或者查询的时候,如果要查到该段的子节点;

4 。就把add加到子节点上去,再将该add设为0; 这样查询子区间的复杂度就是更新的复杂度; 线段树的基础操作主要有5个:建树、单点查询、单点修改、区间查询、区间修改。*/

有两种解法:线段树和树状数组

—,线段树

/为了实现这个,引入一个新的状态——懒标记。

1、直观理解:“懒”标记,懒嘛!用到它才动,不用它就睡觉。

2、作用:存储到这个节点的修改信息,暂时不把修改信息传到子节点。就像家长扣零花钱,你用的时候才给你,不用不给你。 3、实现思路(重点):a.原结构体中增加新的变量,存储这个懒标记。 b.递归到这个节点时,只更新这个节点的状态,并把当前的更改值累积到标记中。 注意是累积, c.什么时候才用到这个懒标记?当需要递归这个节点的子节点时,标记下传给子节 点。这里不必管用哪个子节点,两个都传下去。

d.下传操作: ①当前节点的懒标记累积到子节点的懒标记中。 ②修改子节点状态。在引例中,就是原状态+子节点区间点的个数*父节点传下来的懒标记。 这就有疑问了,既然父节点都把标记传下来了,为什么还要乘父节点的懒标记,乘自 己的不行吗? 因为自己的标记可能是父节点多次传下来的累积,每次都乘自己的懒标记造成重复累积 ③父节点懒标记清0。这个懒标记已经传下去了,不清0后面再用这个懒标记时会重复 下传。

需要注意:1,懒惰标记数组一定要初始化。2. 数据范围*4.

#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
#define ll long long
#define N 400010 //线段树开4倍空间,有时需要离散化让空间压缩
using namespace std;
ll Sum[N],add[N];//add 为懒惰标记,用到它才动,不用它就睡觉
void build(int l,int r,int o)
{if(l==r)/*如果是叶子节点,存储要维护的信息*/{scanf("%lld",&Sum[o]);return;}int mid=(l+r)>>1;/*对于二分到的每一个结点,给它的左右端点确定范围。*/build(l,mid,o<<1);build(mid+1,r,o<<1|1);/*care 走左儿子mid+1*/Sum[o]=Sum[o<<1]+Sum[o<<1|1];//状态合并,把当前结点的信息更新到父结点
}
void pushdown(int o,int l)//把当前结点的信息更新给儿子结点
{if(add[o])//懒惰标记下移{/*such as 1—>7,变为1—>4{4-1+1}=(7-(7>>1))和5—>7{7-5+1}=(7>>1)*/add[o<<1]+=add[o];add[o<<1|1]+=add[o];Sum[o<<1]+=add[o]*(l-(l>>1)); //左子树长度等于总长度减去右子树长度Sum[o<<1|1]+=add[o]*(l>>1); //向下取整  右子树的长度小于等于左子树的长度add[o]=0;}
}
void update(int x,int y,int l,int r,int o,int c)//区间更新
{if(l>=x&&r<=y){Sum[o]+=c*(r-l+1);add[o]+=c;/*求和+=,若对值覆盖=*/return;}pushdown(o,r-l+1);int mid=(l+r)>>1;if(x<=mid)update(x,y,l,mid,o<<1,c);if(y>mid)update(x,y,mid+1,r,o<<1|1,c);Sum[o]=Sum[o<<1]+Sum[o<<1|1];/*对节点数值进行维护*/
}
ll query(int x,int y,int l,int r,int o)//区间查询
{if(l>=x&&r<=y)/*用>=和<=,因为可能我们随机生成的数,不在树节点维护的区间上,若==导致死循环*/return Sum[o];pushdown(o,r-l+1);int mid=(l+r)>>1;ll sum=0;if(x<=mid)sum=query(x,y,l,mid,o<<1);if(y>mid)sum+=query(x,y,mid+1,r,o<<1|1);/*节点的数值等于左儿子加上右儿子的和*/return sum;
}
int main()
{int n,m;while(~scanf("%d%d",&n,&m)){memset(Sum,0,sizeof(Sum));memset(add,0,sizeof(add));//将懒惰标记的值初始化为0build(1,n,1);while(m--){char str[10];int l,r,c;scanf("%s",str);if(str[0]=='Q'){scanf("%d%d",&l,&r);printf("%lld\n",query(l,r,1,n,1));}else{scanf("%d%d%d",&l,&r,&c);update(l,r,1,n,1,c);/*也可开结构体记录1节点下标,节点维护区间*/}}}
}

二,树状数组

区间更新
这就是第一个问题,如果题目是让你把x-y区间内的所有值全部加上k或者减去k,
然后查询操作是问某个点的值,这种时候该怎么做呢。如果是像上面的树状数组
来说,就必须把x-y区间内每个值都更新,这样的复杂度肯定是不行的,这个时候
,就不能再用数据的值建树了,这里我们引入差分,利用差分建树。
假设我们规定A[0] = 0;
则有 A[i] = Σij = 1D[j];(D[j] = A[j] - A[j-1]),即前面i项的差值和,这个有
什么用呢?例如对于下面这个数组
A[] = 1 2 3 5 6 9
D[] = 1 1 1 2 1 3
如果我们把[2,5]区间内值加上2,则变成了
A[] = 1 4 5 7 8 9
D[] = 1 3 1 2 1 1
发现了没有,当某个区间[x,y]值改变了,区间内的差值是不变的,只有D[x]和D[y+1]的值发生改变
所以我们就可以利用这个性质对D[]数组建立树状数组,
区间查询
上面我们说的差值建树状数组,得到的是某个点的值,那如果我既要区间更新,又
要区间查询怎么办。这里我们还是利用差分,由上面可知
则A[1]+A[2]+...+A[n]
= (D[1]) + (D[1]+D[2]) + ... + (D[1]+D[2]+...+D[n])
= n*D[1] + (n-1)*D[2] +... +D[n]
= n * (D[1]+D[2]+...+D[n]) - (0*D[1]+1*D[2]+...+(n-1)*D[n])
如果你理解前面的都比较轻松的话,这里也就知道要干嘛了,维护两个数状数组,
sum1[i] = D[i],sum2[i] = D[i]*(i-1);
#include<iostream>
#include<string.h>
#include<string>
#define kk 100010
using namespace std;
int n,m;
long long a[kk],c[kk];///对应原数组和树状数组[表示差值](D[1] + D[2] + ... + D[n])
long long w[kk]; ///树状数组【要剪的差值(根据推出的公式)】(0*D[1] + 1*D[2] + ... + (n-1)*D[n])
void updata(int i,long long k)     ///在i位置加上k
{int x=i;//因为x不变,所以得先保存i值for(; i <= m; i+=i&(-i)){c[i] += k;w[i]+=k*(x-1);}
}
long long query(int i)         ///求A[1 - i]的和(前缀和)
{long long res = 0;int x=i;for(; i > 0; i-=i&(-i))res += x*c[i]-w[i];return res;
}
int main()
{std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);while(cin>>m>>n){memset(a, 0, sizeof a);memset(c, 0, sizeof c);memset(w,0,sizeof(w));for(int i = 1; i <= m; i++){cin>>a[i];updata(i,a[i]-a[i-1]);   ///输入初值的时候,也相当于更新了值}string s;int x,y;long long z;while( n--){cin>>s;if(s[0] == 'Q')     ///求和操作{cin>>x>>y;long long sum = query(y) - query(x-1);    //x-y区间和也就等于1-y区间和减去1-(x-1)区间和cout << sum << endl;}else if(s[0] == 'C')  ///[x,y]区间内加上z{cin>>x>>y>>z;updata(x,z);///A[x] - A[x-1]增加zupdata(y+1,-z); ///A[y+1] - A[y]减少z}}}return 0;
}
这里利用的负数的存储特性,负数是以补码存储的,对于整数运算 x&(-x)有
● 当x为0时,即 0 & 0,结果为0;
●当x为奇数时,最后一个比特位为1,取反加1没有进位,故x和-x除最后一位外前
面的位正好相反,按位与结果为0。结果为1。
●当x为偶数,且为2的m次方时,x的二进制表示中只有一位是1(从右往左的第m+1
位),其右边有m位0,故x取反加1后,从右到左第有m个0,第m+1位及其左边全是1
。这样,x& (-x) 得到的就是x。
●当x为偶数,却不为2的m次方的形式时,可以写作x= y * (2^k)。其中,y的最低
位为1。实际上就是把x用一个奇数左移k位来表示。这时,x的二进制表示最右边有
k个0,从右往左第k+1位为1。当对x取反时,最右边的k位0变成1,第k+1位变为0
;再加1,最右边的k位就又变成了0,第k+1位因为进位的关系变成了1。左边的位
因为没有进位,正好和x原来对应的位上的值相反。二者按位与,得到:第k+1位上
为1,左边右边都为0。结果为2^k。
总结一下:x&(-x),当x为0时结果为0;x为奇数时,结果为1;x为偶数时,结果为
x中2的最大次方的因子。
而且这个有一个专门的称呼,叫做lowbit,即取2^k。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 kind 快速搭建一个 Kubernetes 测试环境

使用 kind 快速搭建一个 Kubernetes 测试环境Introkind&#xff08;Kubernetes IN Docker&#xff09; 是一个基于 docker 构建 Kubernetes 集群的工具&#xff0c;非常适合用来在本地搭建基于 Kubernetes 的开发/测试环境。想写一篇 kind 的文章很久了&#xff0c;但是之前的 …

[PAT乙级]1013 数素数

令 P​i​​ 表示第 i 个素数。现任给两个正整数 M≤N≤10​4​​&#xff0c;请输出 P​M​​ 到 P​N​​ 的所有素数。 输入格式&#xff1a; 输入在一行中给出 M 和 N&#xff0c;其间以空格分隔。 输出格式&#xff1a; 输出从 P​M​​ 到 P​N​​ 的所有素数&#xf…

D. 关灯问题(规律或二分)

题目描述 今年就这么结束了, zdw感到十分失望 蓝桥杯写错签到题, cf rating狂掉, 最后区域赛打铜, 还突然变成JBer了 失落的zdw准备睡觉(真咸鱼), 他想关灯, 然而发现开关坏了 zdw愤怒地敲击着开关, 然后发现一个很神奇的事情: 如果灯之前已经关过了xx次, 那么下一次打开它…

麻雀虽小,五脏俱全

入职三年&#xff0c; 除了参与公司核心产品研发外&#xff0c;另外负责了一个2C的小项目&#xff1a;调用API拿到解析结果 & 计费。❝项目最初是.NetCore 1.0-Previewsqlite部署在IIS上&#xff0c;闲来没事&#xff0c;这个项目已经被我完全重写&#xff0c;在此记录一些…

[PAT乙级]1016 部分A+B

正整数 A 的“D​A​​&#xff08;为 1 位整数&#xff09;部分”定义为由 A 中所有 D​A​​ 组成的新整数 P​A​​。例如&#xff1a;给定 A3862767&#xff0c;D​A​​6&#xff0c;则 A 的“6 部分”P​A​​ 是 66&#xff0c;因为 A 中有 2 个 6。 现给定 A、D​A​​…

java循环语句_java中循环语句

java中的循环语句主要包括while (){}语句&#xff0c;for(){}语句&#xff0c;do{}while()语句一、while语句&#xff1a;while语句的使用情况是不知道循环次数的是使用。格式&#xff1a;while(判断条件){循环体&#xff1b;}解读&#xff1a;当条件为真时&#xff0c;会执行循…

内存迟迟下不去,可能你就差一个GC.Collect

一&#xff1a;背景1. 讲故事我们有一家top级的淘品牌店铺&#xff0c;为了后续的加速计算&#xff0c;在程序启动的时候灌入她家的核心数据到内存中&#xff0c;灌入完成后内存高达100G&#xff0c;虽然云上的机器内存有256G&#xff0c;然被这么划掉一半看着还是有一点心疼的…

Mayor's posters POJ - 2528 (离散化+线段树)

题意&#xff1a; 在1~10000000这个区间中读取n个海报的区间信息&#xff0c;后面的海报会覆 盖前面的海报&#xff0c;问最后能看到几张海报.&#xff08;本题是一道bug题下面会提&#xff09; 题目&#xff1a; The citizens of Bytetown, AB, could not stand that the c…

[PAT乙级]1017 A除以B

本题要求计算 A/B&#xff0c;其中 A 是不超过 1000 位的正整数&#xff0c;B 是 1 位正整数。你需要输出商数 Q 和余数 R&#xff0c;使得 ABQR 成立。 输入格式&#xff1a; 输入在一行中依次给出 A 和 B&#xff0c;中间以 1 空格分隔。 输出格式&#xff1a; 在一行中依次…

服务器程序的Xamarin-Java.Interop体验(一)

这几天需要写一个用到Java模块的程序&#xff0c;但是Java是不可能写的&#xff0c;这辈子都不可能写的&#xff0c;只能搞搞interop了。目前市面上已有的基本上是IKVM.NET和JNBridgePro&#xff0c;后者没太了解技术细节&#xff0c;前者看起来是只有单向的互操作&#xff08;…

A Walk Through the Forest HDU - 1142(dijkstra+动态规划)

题目大意&#xff1a; 给你一个图&#xff0c;找最短路。但是有个非一般的的条件&#xff1a;如果a,b之间有路&#xff0c; 且你选择要走这条路&#xff0c;那么必须保证a到终点的所有路都小于b到终点的 一条路。问满足这样的路径条数 有多少。。。 解题思路 &#xff1a; 1.…

[PAT乙级]1018 锤子剪刀布

大家应该都会玩“锤子剪刀布”的游戏&#xff1a;两人同时给出手势&#xff0c;胜负规则如图所示&#xff1a; 现给出两人的交锋记录&#xff0c;请统计双方的胜、平、负次数&#xff0c;并且给出双方分别出什么手势的胜算最大。 输入格式&#xff1a; 输入第 1 行给出正整数 …

java 开源控件_一些好用的开源控件

工作两年&#xff0c;一直都在做些编码方面的表面功夫&#xff0c;实现了很多很炫的功能&#xff0c;在此写下一些体验。有些比较小的dll文件我会发上来&#xff0c;如果是开源组织的代码我会把地址附上&#xff0c;毕竟人家是会更新的。大家还有什么好用的开源控件欢迎补充。一…

揭秘 .NET 5 和Java 互操作

早早的.NET团队就立下了.NET和Java互操作的flag&#xff0c;如果你去翻一翻dotnet/runtime库&#xff0c;丝毫看不出来仓库内在搞支持。xamarin/java.interop库一直有Mono和Java互操作的实现&#xff0c;那么100%的实现.NET和Java互操作就是它&#xff0c;这两篇文章就是和你一…

[PAT乙级]1019 数字黑洞

给定任一个各位数字不完全相同的 4 位正整数&#xff0c;如果我们先把 4 个数字按非递增排序&#xff0c;再按非递减排序&#xff0c;然后用第 1 个数字减第 2 个数字&#xff0c;将得到一个新的数字。一直重复这样做&#xff0c;我们很快会停在有“数字黑洞”之称的 6174&…

Ice_cream's world I HDU - 2120(并查集判环)

题意&#xff1a;问给出的望塔之间的建造了围墙&#xff0c;将土地分成了几份 思路&#xff1a;用并查集判环&#xff0c;若有围墙相接的瞭望塔&#xff0c;有相同的父根&#xff0c;则存在环 ice_creams world is a rich country, it has many fertile lands. Today, the qu…

java 普通类获取session_如何在Java的普通类中获取Session以及request对象

展开全部你是指你的 web 项目中使用到一个工具性的类&#xff0c;它的e69da5e6ba9062616964757a686964616f31333337613865形参中没有 HttpRequest 或 HttpSession 参数&#xff1f;如果是这样的话&#xff0c;我们需要使用一个 ThreadLocal 变量&#xff0c;我们把当前 reques…

[PAT乙级]1020 月饼

月饼是中国人在中秋佳节时吃的一种传统食品&#xff0c;不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量&#xff0c;请你计算可以获得的最大收益是多少。 注意&#xff1a;销售时允许取出一部分库存。样例给出的情形是这样的&#…

java中的asList_Java Arrays.AsList原理及用法实例

java.util.Arrays的asList方法可以方便的将数组转化为集合&#xff0c;我们平时开发在初始化ArrayList时使用的比较多&#xff0c;可以简化代码&#xff0c;但这个静态方法asList()有几个坑需要注意:一. 如果对集合使用增加或删除元素的操作将会报错如下代码&#xff1a;List l…

为什么顶尖高手,都是长期主义者?

点击蓝字关注&#xff0c;回复“职场进阶”获取职场进阶精品资料一份在当下这个浮躁的社会&#xff0c;很多人都认为“快”才是真理&#xff0c;“慢”就是原罪。大家都对一夜腾飞的故事津津乐道&#xff0c;却觉得坚守一生最终获得大成的故事不够性感。但事实是怎么样的呢&…