#6280. 数列分块入门 4 #6281. 数列分块入门 5
Posted xiongtao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#6280. 数列分块入门 4 #6281. 数列分块入门 5相关的知识,希望对你有一定的参考价值。
题目描述
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,区间求和。
输入格式
第一行输入一个数字 n。
第二行输入 n 个数字,第 i 个数字为 ai?,以空格隔开。
接下来输入 n 行询问,每行输入四个数字 opt、l、r、c,以空格隔开。
若 opt=0,表示将位于 [l,r]的之间的数字都加 c。
若 opt=1,表示询问位于 [l,r]的所有数字的和 ?mod(c+1)。
输出格式
对于每次询问,输出一行一个数字表示答案。
样例
样例输入
4
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4
样例输出
1
4
思路:分块,不是整块的暴力加,整块的用sum[]数组维护整块的和。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<vector> #include<cmath> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=5e4+10; ll a[maxn],b[maxn],sum[maxn],pos[maxn],n,block; ll ans; void update(int l,int r,int c)//更新 { for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左边不完整的块暴力加,sum数组维护 { sum[pos[i]]+=c; a[i]+=c; } if(pos[l]!=pos[r]) { for(int i=(pos[r]-1)*block+1;i<=r;i++)//右边不完整的块暴力加,sum数组维护 { sum[pos[i]]+=c; a[i]+=c; } } for(int i=pos[l]+1;i<=pos[r]-1;i++)//整块的b数组保存 b[i]+=c; } ll query(int l,int r,int mod)//查询 { ans=0; for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左边不完整的块,暴力加每个数 ans+=a[i]+b[pos[l]]; if(pos[l]!=pos[r]) { for(int i=(pos[r]-1)*block+1;i<=r;i++)//右边不完整的块,暴力加每个数 ans+=a[i]+b[pos[r]]; } for(int i=pos[l]+1;i<=pos[r]-1;i++)//完整的块整块加 { ans+=sum[i]+b[i]*block; } return ans%mod; } int main() { scanf("%d",&n); memset(sum,0,sizeof(sum)); block=sqrt(n);//块的大小 for(int i=1;i<=n;i++) { scanf("%d",&a[i]); pos[i]=(i-1)/block+1; sum[pos[i]]+=a[i];//sum数组维护整块的和 } int opt,l,r,c; for(int i=0;i<n;i++) { scanf("%d%d%d%d",&opt,&l,&r,&c); if(opt==0) update(l,r,c); else if(opt==1) printf("%lld ",query(l,r,c+1)); } return 0; }
第5题:
思路:这里有个小思维,因为数的大小小于2^31,最多开方5次就为1。所以我们做法和上面一样有sum数组维护整块的值,
计算整块的时候,用flag记录整组都为1的情况,如果全为1就加block(块的大小),不是就暴力加,不会超时的,因为最多5次开方
就全为1了,时间复杂度和上面的一样(5倍对时间复杂度没关系)
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #define inf 0x3f3f3f using namespace std; typedef long long ll; const int maxn=5e4+10; ll a[maxn],pos[maxn],sum[maxn],flag[maxn],block,n; //a数组存储数字,pos记录每个数在哪个块,sum维护整块的和,flag标记整块是否全为1,blocK块的大小。 void update(int l,int r)//更新 { for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左边不完整块,暴力加。 { sum[pos[l]]-=a[i]; a[i]=sqrt(a[i]); sum[pos[l]]+=a[i];//sum数组记得改动 } if(pos[l]!=pos[r])//右边不完整块,暴力加。 { for(int i=(pos[r]-1)*block+1;i<=r;i++) { sum[pos[r]]-=a[i]; a[i]=sqrt(a[i]); sum[pos[r]]+=a[i]; } } for(int i=pos[l]+1;i<=pos[r]-1;i++)//整块 { if(flag[i])//全为1,直接跳过 continue; else//不是全为1,暴力处理 { flag[i]=1;//先假设全为1,后面如果不是改为0 for(int j=(i-1)*block+1;j<=i*block;j++) { sum[i]-=a[j]; a[j]=sqrt(a[j]); sum[i]+=a[j]; if(a[j]!=1)//不是全为1 flag[i]=0; } } } } void solve(int l,int r) { ll ans=0; for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左边不完整块,暴力加 ans+=a[i]; if(pos[l]!=pos[r]) { for(int i=(pos[r]-1)*block+1;i<=r;i++)//右边不完整块,暴力加 ans+=a[i]; } for(int i=pos[l]+1;i<=pos[r]-1;i++)//完整块直接加,整块的和即可 ans+=sum[i]; printf("%lld ",ans); } int main() { scanf("%d",&n); block=sqrt(n);//块的大小 memset(flag,0,sizeof(flag)); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); pos[i]=(i-1)/block+1; sum[pos[i]]+=a[i];//每块的和 } 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); else solve(l,r); } return 0; }
以上是关于#6280. 数列分块入门 4 #6281. 数列分块入门 5的主要内容,如果未能解决你的问题,请参考以下文章