#6277. 数列分块入门 1
Posted xiongtao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#6277. 数列分块入门 1相关的知识,希望对你有一定的参考价值。
输入格式
第一行输入一个数字 nnn。
第二行输入 nnn 个数字,第 iii 个数字为 aia_iai?,以空格隔开。
接下来输入 nnn 行询问,每行输入四个数字 optmathrm{opt}opt、lll、rrr、ccc,以空格隔开。
若 opt=0mathrm{opt} = 0opt=0,表示将位于 [l,r][l, r][l,r] 的之间的数字都加 ccc。
若 opt=1mathrm{opt} = 1opt=1,表示询问 ara_rar? 的值(lll 和 ccc 忽略)。
输出格式
对于每次询问,输出一行一个数字表示答案。
样例
样例输入
4
1 2 2 3
0 1 3 1
1 0 1 0
0 1 2 2
1 0 2 0
样例输出
2
5
数据范围与提示
对于100%的数据,1<=n<=50000, -2^31<=others,ans<=2^31-1。
思路:
1.用线段树或者树状数组作,这里写了一个线段树的,懒标记,区间更新的模板题,不多讲
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=5e4+10; int tree[maxn*4],lazy[maxn*4],n; void build(int l,int r,int root)//建树 { if(l==r) { scanf("%d",&tree[root]); return; } int mid=(l+r)>>1; build(l,mid,root<<1); build(mid+1,r,root<<1|1); tree[root]=tree[root<<1]+tree[root<<1|1]; } void pushdown(int l,int r,int k)//懒标记下传 { if(lazy[k]) { int mid=(l+r)>>1; lazy[k<<1]+=lazy[k]; lazy[k<<1|1]+=lazy[k]; tree[k<<1]+=(mid-l+1)*lazy[k]; tree[k<<1|1]+=(r-mid)*lazy[k]; lazy[k]=0; } } void update(int x,int y,int l,int r,int root,int val)//区间更新 { if(x<=l&&y>=r) { lazy[root]+=val; tree[root]+=val*(r-l+1); return ; } pushdown(l,r,root); int mid=(l+r)>>1; if(x<=mid) update(x,y,l,mid,root<<1,val); if(y>mid) update(x,y,mid+1,r,root<<1|1,val); tree[root]=tree[root<<1]+tree[root<<1|1]; } int query(int l,int r,int root,int x)//查询 { if(l==r) return tree[root]; pushdown(l,r,root); int mid=(l+r)>>1; int ans; if(x<=mid) ans=query(l,mid,root<<1,x); if(x>mid) ans=query(mid+1,r,root<<1|1,x); tree[root]=tree[root<<1]+tree[root<<1|1]; return ans; } int main() { scanf("%d",&n); build(1,n,1); int opt,l,r,c; for(int i=1;i<=n;i++) { scanf("%d%d%d%d",&opt,&l,&r,&c); if(opt==1) printf("%d ",query(1,n,1,r)); else if(opt==0) update(l,r,1,n,1,c); } return 0; }
2.分块思维:
我们假设每个块大小设为m,则有n/m个块,对于不在完整块里面的区间,就直接暴力更新,因为每个块的大小不超过m,所以复杂度是2m,所以每次更新的复杂度是O(n/m + m)由均值不等式可知,当m等于√n时,复杂度最小。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=5e4+10; int n,block,a[maxn],b[maxn],pos[maxn]; void update(int l,int r,int c) { for(int i=l;i<=min(pos[l]*block,r);i++)//左边的不完整的块暴力加 a[i]+=c; if(pos[l]!=pos[r]) { for(int i=(pos[r]-1)*block+1;i<=r;i++)//右边不完整的块的暴力加 a[i]+=c; } for(int i=pos[l]+1;i<=pos[r]-1;i++)//l到r中间的完整块,可以整块用b记录。 b[i]+=c; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); block=sqrt(n);//分块,块的大小 for(int i=1;i<=n;i++) pos[i]=(i-1)/block+1;//每个数处在哪个块中 int opt,l,r,c; for(int i=1;i<=n;i++) { scanf("%d%d%d%d",&opt,&l,&r,&c); if(opt==0) update(l,r,c); else if(opt==1) printf("%d ",a[r]+b[pos[r]]); } return 0; }
以上是关于#6277. 数列分块入门 1的主要内容,如果未能解决你的问题,请参考以下文章