涉及区间操作的一些套路必须要会呀

区间加减为了偷懒能不写线段树so我选择树状数组!!

但是区间乘除,最大值我想了想还是用线段树分块吧。

树状数组:

这里用网上的一张图:

这里灰色数组是原本的数组(a[i])红色数组则是树状数组(c[i])这里直接给出结论:

c[i]=a[i-2^k+range[1,2^k]]

k是i的二进制位从低到高位连续0的个数

与a[i]有关的 c[i+2^(k+j)] 且 i+2^(j+k)<n

这样就很好实现单点更改,区间查询了。

void lowbit(int x)
{
return x&(-x);//求2^x
}
void updata(int x,int val)//在x处加val
{
while(x<=n)
{
c[i]+=val;
x+=lowbit(x);//x在不断变化要不断求lowbit
}
}
int getsum(x)//求a[1~x]的和
{
int ans=;
while(x)
{
ans+=c[x];
x-=lowbit(x);
}
return ans;
}

  

区间查询怎么搞呢?

这里可以用到差分数组

比如在a=[1,5,7,2,3,7,1]则有的差分数组d=[1,4,2,-5,1,4,-6](a[i]-a[i-1)a[0]=0,sum[d[1~i]]=a[i]

在区间x,y全部加k则可以在d[x]+k,d[y+1]-k 则在求1~i i in range[x,y]这部分区间的和即可得到a[i]+k。这样就变成了单点修改区间查询了

这样实际上a数组是没有用的,’a‘则变成了d数组,对d数组构造树状数组c

这样就实现了区间修改,单点查询:

单点查询模板 https://www.luogu.com.cn/problem/P3368

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=5e5+;
int n,m,q,p,w,x,ans,a[N],c[N];
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int val)//x点+val值
{
while(x<=n)
{
c[x]+=val;
x+=lowbit(x);//x在不断变化要不断求lowbit
}
}
int getsum(int x)//求'a'[1~x]的和
{
int res=;
while(x)
{
res+=c[x];
x-=lowbit(x);
}
return res;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
update(i,a[i]-a[i-]);//建立差分数组d
}
for(int i=;i<=m;i++)
{
scanf("%d",&x);
if(x==)
{
scanf("%d %d %d",&q,&p,&w);//q,p区间+w
update(q,w);
update(p+,-w);
}
else
{
scanf("%d",&q);
ans=getsum(q); //求d的1~q的和即为a[q]
printf("%d\n",ans);
}
}
return ;
}

区间修改怎么搞呢?

sum[a[1~n]]=d[1]+d[1~2]+...+d[1~n]=n*d[1]+(n-1)d[2]+...+d[n]=n*d[1~n]-(0*d[1]+1*d[2]+...+(n-1)*d[n])(在树状数组里就是另一种表示,这里只是普通数组表示)

可见两部分变量可写成=n*sum1[n]-sum2[n](又是区间求和了)

永远要记住求谁的和构建谁的树状数组然后再树状数组求和,因为优化是树状数组求和造成的

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=5e5+;
int n,m,q,p,w,x,ans,a[N],c1[N],c2[N];
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int val)//x点+val值
{
int i=x;
while(i<=n)
{
c1[i]+=val;//c1就是d数组的树状数组
c2[i]+=(x-)*val; //c2是d[i]*(i-1)的树状数组
i+=lowbit(i);//i要不断变化所以要不断求lowbit
}
}
int getsum(int x)//求a[1~x]的和
{
int res=,res1=,res2=,i=x;
while(i)
{
res1+=c1[i]*x;//求d[1~x](sum1)根据分配律可直接求得sum1*n
res2+=c2[i];//求sum2
//res+=c[i]*x-c2[i];就是单纯的求a[1~x]
i-=lowbit(i);
}
res=res1-res2;//a[1~x]
return res;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
update(i,a[i]-a[i-]);//建立
}
for(int i=;i<=m;i++)
{
scanf("%d",&x);
if(x==)
{
scanf("%d %d %d",&q,&p,&w);//q,p区间+w
update(q,w);
update(p+,-w);
}
else
{
scanf("%d",&q);
ans=getsum(q)-getsum(q-);
printf("%d\n",ans);
}
}
return ;
}

  

线段树整合:

忘不了的线段树开四倍

区间加减,乘除,最大值:

代码还是过那个模板题的

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=5e5+;
int n,m,q,p,w,x,ans,a[N];
struct node
{
int sum,add,mul;//add,mul是lazy_tag
}t[N*];
void push_down(int p)
{
t[p].sum=t[p*].sum+t[p*+].sum;//%mod
return ;
}
void built(int p,int l,int r)
{
t[p].add=;
t[p].mul=;
if(l==r)
{
t[p].sum=a[l];
//mod;
return ;
}
int mid=(l+r)/;
built(p*,l,mid);
built(p*+,mid+,r);
push_down(p);
return ;
}
int askmin(int p,int qx,int zx,int gl,int gr)
{
if(qx>=gl&&zx<=gl)
{
return t[p].minn;
}
int mid=(qx+zx)/,ans=;
if(gl<=mid)
ans=min(ans,askmin(p*,qx,mid,gl,gr));
if(gr>mid)
ans=min(ans,askmin(p*+,mid+,zx,gl,gr));
return ans;
}
void push_tag(int p,int l,int r)
{
if(t[p].mul!=)//必须先更新乘法
{
t[p*].sum*=t[p].mul;//mod
t[p*+].sum*=t[p].mul;
t[p*].add*=t[p].mul;//mod
t[p*+].add*=t[p].mul;
t[p*].mul*=t[p].mul;//mod
t[p*+].mul*=t[p].mul;
t[p].mul=;
}
if(t[p].add)//加不会对乘的tag产生影响
{
int mid=(l+r)/;
t[p*].add+=t[p].add;//mod
t[p*+].add+=t[p].add;
t[p*].sum+=(mid-l+)*t[p].add;//mod
t[p*+].sum+=(r-mid)*t[p].add;
t[p].add=;
}
return ;
}
void add(int p,int qx,int zx,int gl,int gr,int k)//区间加减
{
if(qx>=gl&&zx<=gr)
{
t[p].sum+=(zx-qx+)*k;
t[p].add+=k;//mod
return ;
}
int mid=(qx+zx)/;
push_tag(p,qx,zx);
if(gl<=mid)
add(p*,qx,mid,gl,gr,k);
if(gr>mid)
add(p*+,mid+,zx,gl,gr,k);
push_down(p);
return ;
}
void mult(int p,int qx,int zx,int gl,int gr,int k)//区间乘
{
if(qx>=gl&&zx<=gr)
{
t[p].sum*=k;
t[p].add*=k;
t[p].mul*=k;//mod
return ;
}
int mid=(qx+zx)/;
if(gl<=mid)
mult(p*,qx,mid,gl,gr,k);
if(gr>mid)
mult(p*+,mid+,zx,gl,gr,k);
}
int ask(int p,int qx,int zx,int gl,int gr)
{
if(qx>=gl&&zx<=gr)
return t[p].sum;//mod
int ans=,mid=(qx+zx)/;
push_tag(p,qx,zx);
if(gl<=mid)
ans+=ask(p*,qx,mid,gl,gr);
if(gr>mid)
ans+=ask(p*+,mid+,zx,gl,gr);
return ans;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=;i<=n;i++)
scanf("%d",&a[i]);
built(,,n);
for(int i=;i<=m;i++)
{
scanf("%d",&x);
if(x==)
{
scanf("%d %d %d",&q,&p,&w);//q,p区间+w
add(,,n,q,p,w);
}
if(x==)
{
scanf("%d",&q);
printf("%d\n",ask(,,n,q,q));
}
}
return ;
}

  

区间操作---树状数组&&线段树的更多相关文章

  1. hdu1394(枚举/树状数组/线段树单点更新&区间求和)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题意:给出一个循环数组,求其逆序对最少为多少: 思路:对于逆序对: 交换两个相邻数,逆序数 +1 ...

  2. 洛谷P2414 阿狸的打字机 [NOI2011] AC自动机+树状数组/线段树

    正解:AC自动机+树状数组/线段树 解题报告: 传送门! 这道题,首先想到暴力思路还是不难的,首先看到y有那么多个,菜鸡如我还不怎么会可持久化之类的,那就直接排个序什么的然后按顺序做就好,这样听说有7 ...

  3. 树状数组 && 线段树应用 -- 求逆序数

    参考:算法学习(二)——树状数组求逆序数 .线段树或树状数组求逆序数(附例题) 应用树状数组 || 线段树求逆序数是一种很巧妙的技巧,这个技巧的关键在于如何把原来单纯的求区间和操作转换为 求小于等于a ...

  4. hdu 1166:敌兵布阵(树状数组 / 线段树,入门练习题)

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  5. hdu 5147 Sequence II【树状数组/线段树】

    Sequence IITime Limit: 5000/2500 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem ...

  6. 二分+树状数组/线段树(区间更新) HDOJ 4339 Query

    题目传送门 题意:给两串字符串,操作1:替换其中一个字符串的某个位置的字符 操作2:查询从p开始相等的最长连续长度 分析:树状数组可以维护一个区间内公共长度(连续)的情况,查询时用二分查找最远的端点即 ...

  7. 51nod 1680区间求和 (dp+树状数组/线段树)

    不妨考虑已知一个区间[l,r]的k=1.k=2....k=r-l+1这些数的答案ans(只是这一个区间,不包含子区间) 那么如果加入一个新的数字a[i](i = r+1) 则新区间[l, i]的答案为 ...

  8. hdu 1166 敌兵布阵——(区间和)树状数组/线段树

    pid=1166">here:http://acm.hdu.edu.cn/showproblem.php?pid=1166 Input 第一行一个整数T.表示有T组数据. 每组数据第一 ...

  9. BZOJ 3333 排队计划 树状数组+线段树

    题目大意:给定一个序列.每次选择一个位置,把这个位置之后全部小于等于这个数的数抽出来,排序,再插回去,求每次操作后的逆序对数 首先我们每一次操作 对于这个位置前面的数 因为排序的数与前面的数位置关系不 ...

随机推荐

  1. NOI2.5 8465:马走日

    描述 马在中国象棋以日字形规则移动. 请编写一段程序,给定n*m大小的棋盘,以及马的初始位置(x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点. 输入 第一行为整数T ...

  2. Apache Hudi 0.5.1版本重磅发布

    历经大约3个月时间,Apache Hudi 社区终于发布了0.5.1版本,这是Apache Hudi发布的第二个Apache版本,该版本中一些关键点如下 版本升级 将Spark版本从2.1.0升级到2 ...

  3. [SDOI2011]染色(树链剖分)

    [SDOI2011]染色(luogu) Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段 ...

  4. springboot打印sql语句及执行时间

    有时候我们程序的接口比较耗时,需要优化,这时我们可能需要了解该接口执行了哪些sql语句以及耗时 1.引入jar包 <!--监控sql日志--> <dependency> < ...

  5. 【大白话系列】MySQL 学习总结 之 初步了解 InnoDB 存储引擎的架构设计

    一.存储引擎 上节我们最后说到,SQL 的执行计划是执行器组件调用存储引擎的接口来完成的. 那我们可以理解为:MySQL 这个数据库管理系统是依靠存储引擎与存放数据的磁盘文件进行交互的. 那么 MyS ...

  6. pandas使用的25个技巧

      本文翻译自https://nbviewer.jupyter.org/github/justmarkham/pandas-videos/blob/master/top_25_pandas_trick ...

  7. python学习记录(七)

    0904--https://www.cnblogs.com/fnng/archive/2013/04/24/3039335.html 0904--https://www.cnblogs.com/fnn ...

  8. 测试用例设计:PICT的安装及使用

    一.下载与安装 打开百度网页,搜索PICT,即可找到许多下载链接,点击这里,下载到桌面,点击安装.一直NEXT,安装路径保存在C盘: 二.PICT 使用 1.找到安装目录,即可看到以下内容 2.创建t ...

  9. HDU_1394_线段树

    http://acm.hdu.edu.cn/showproblem.php?pid=1394 线段树入门题,每次读入一个数,就寻找在树中比它大的值的个数,然后更新树,把个数相加就是逆序数,每移动一个数 ...

  10. CCF_201409-1_相邻数对

    水. #include<iostream> #include<cstdio> #include<algorithm> using namespace std; in ...