6277. 数列分块入门 1
题面: 传送门
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)
#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int maxn = 1000;
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
int k;
int a[50005],b[50005],tag[50005];
void add(int l,int r,int c){
for(int i=l;i<=min(r,b[l]*k);++i){ //左边不完整块暴力
a[i]+=c;
}
if(b[l]!=b[r]){
for(int i=(b[r]-1)*k+1;i<=r;++i){ //中间块标记一下+c
a[i]+=c;
}
}
for(int i=b[l]+1;i<=b[r]-1;++i){ //右边不完整块暴力
tag[i]+=c;
}
}
int main(){
//fin;
int n=read();
k=sqrt(n);
for(int i=1;i<=n;++i){
a[i]=read();
b[i]=(i-1)/k+1; //记录该位置处于哪个块
}
for(int i=0;i<n;++i){
int op=read(),l=read(),r=read(),x=read();
if(0==op){
add(l,r,x);
}else{
printf("%d\n",a[r]+tag[b[r]]);
}
}
return 0;
}
6278. 数列分块入门 2
题面:传送门
分块思路:首先呢,分成sqrt(n)个块。由于该题目需要查比c*c小的数的个数,
那么我们可以利用二分,所有我们需要预处理每个块,排序一下。
执行add操作时:我们分为三步,左边不完整的块添加c,直接暴力改变值。
中间完整的块我们就做标记,tag[i]表示第i块增加的数量。
右边不完整的块也是暴力改变值即可。左右两个不完整的块改变值后需要对所处的块进行
排序保持其单调性。
执行query操作时,左右不完整块直接暴力查找比c-tag[i]小的即可。
完整的块利用二分即可。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)
#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 1000000000+7;
const int maxn = 1000;
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,k;
int a[50005],b[50005],tag[505];
vector<int> vec[505]; //保持各个块的值
void reset(int x){ //不完整块改变值 需要对该块进行排序保持其单调性
vec[x].clear();
for(int i=(x-1)*k+1;i<=min(x*k,n);++i){
vec[x].push_back(a[i]);
}
sort(vec[x].begin(),vec[x].end());
}
void add(int l,int r,int c){
for(int i=l;i<=min(r,b[l]*k);++i){ //左边的不完整块
a[i]+=c;
}
reset(b[l]);
if(b[l]!=b[r]){
for(int i=(b[r]-1)*k+1;i<=r;++i){ //右边的不完整的块
a[i]+=c;
}
reset(b[r]);
}
for(int i=b[l]+1;i<=b[r]-1;++i){ //中间完整的块 b[l]+1~b[r]-1
tag[i]+=c;
}
}
int query(int l,int r,int c){
int res=0;
for(int i=l;i<=min(r,b[l]*k);++i){
if(a[i]+tag[b[l]]<c){
++res;
}
}
if(b[l]!=b[r]){
for(int i=(b[r]-1)*k+1;i<=r;++i){
if(a[i]+tag[b[r]]<c){
++res;
}
}
}
for(int i=b[l]+1;i<=b[r]-1;++i){
int x=c-tag[i];
res+=lower_bound(vec[i].begin(),vec[i].end(),x)-vec[i].begin();
}
return res;
}
int main(){
n=read(),k=sqrt(n);
for(int i=1;i<=n;++i){
a[i]=read();
}
for(int i=1;i<=n;++i){
b[i]=(i-1)/k+1;
vec[b[i]].push_back(a[i]);
}
for(int i=1;i<=b[n];++i){
sort(vec[i].begin(),vec[i].end());
}
for(int i=1;i<=n;++i){
int op=read(),l=read(),r=read(),c=read();
if(0==op){
add(l,r,c);
}else{
printf("%d\n",query(l,r,c*c));
}
}
return 0;
}
6277. 数列分块入门 3
题面传送门
分块思路:同题面二其实是差不多的,只是这次是找c的前驱(即小于c的最大值),利用set来维护
块的块区间的单调性。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)
#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 1000000000+7;
const int maxn = 1000;
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,k;
int v[100010],b[100010],tag[505];
set<int> st[505];
inline void add(int l,int r,int c){
for(int i=l;i<=min(b[l]*k,r);++i){
st[b[l]].erase(v[i]);
v[i]+=c;
st[b[l]].insert(v[i]);
}
if(b[l]!=b[r]){
for(int i=(b[r]-1)*k+1;i<=r;++i){
st[b[r]].erase(v[i]);
v[i]+=c;
st[b[r]].insert(v[i]);
}
}
for(int i=b[l]+1;i<=b[r]-1;++i){
tag[i]+=c;
}
}
inline int query(int l,int r,int c){
int ans=-1;
for(int i=l;i<=min(r,b[l]*k);++i){
int x=v[i]+tag[b[l]];
if(x<c){
ans=max(ans,x);
}
}
if(b[l]!=b[r]){
for(int i=(b[r]-1)*k+1;i<=r;++i){
int x=v[i]+tag[b[r]];
if(x<c){
ans=max(ans,x);
}
}
}
for(int i=b[l]+1;i<=b[r]-1;++i){
int x=c-tag[i];
set<int>::iterator it=st[i].lower_bound(x); //找比x小的
if(it==st[i].begin()){
continue;
}
--it;
ans=max(ans,*it+tag[i]);
}
return ans;
}
int main(){
fin;
n=read(),k=sqrt(n);
for(int i=1;i<=n;++i){
v[i]=read();
}
for(int i=1;i<=n;++i){
b[i]=(i-1)/k+1;
st[b[i]].insert(v[i]);
}
for(set<int>::iterator it=st[1].begin();it!=st[1].end();++it){
cout<<*it<<endl;
}
for(int i=1;i<=n;++i){
int opt=read(),l=read(),r=read(),c=read();
if(0==opt){
add(l,r,c);
}else{
printf("%d\n",query(l,r,c));
}
}
return 0;
}
6277. 数列分块入门 4
题面传送门
分块思路:区间加法,区间求和。
tag数组标记块区间累加的c
sum数组维护该块区间的区间和,记得long long类型。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)
#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 1000000000+7;
const int maxn = 1000;
using namespace std;
inline ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,k;
int b[50010];
ll v[50010],tag[505];
ll sum[505];
inline void add(int l,int r,int c){
for(int i=l;i<=min(b[l]*k,r);++i){
v[i]+=c; //更新值
sum[b[l]]+=c; //更新该块的区间和
}
if(b[l]!=b[r]){
for(int i=(b[r]-1)*k+1;i<=r;++i){
v[i]+=c;
sum[b[r]]+=c;
}
}
for(int i=b[l]+1;i<=b[r]-1;++i){
tag[i]+=c;
}
}
inline ll query(int l,int r){
ll ans=0;
for(int i=l;i<=min(b[l]*k,r);++i){
ans+=v[i]+tag[b[l]];
}
if(b[l]!=b[r]){
for(int i=(b[r]-1)*k+1;i<=r;++i){
ans+=v[i]+tag[b[r]];
}
}
for(int i=b[l]+1;i<=b[r]-1;++i){
ll t=tag[i]*k;
ans+=sum[i]+t;
}
return ans;
}
int main(){
n=read(),k=sqrt(n);
for(int i=1;i<=n;++i){
v[i]=read();
}
for(int i=1;i<=n;++i){
b[i]=(i-1)/k+1;
sum[b[i]]+=v[i];
}
for(int i=1;i<=n;++i){
int opt=read(),l=read(),r=read(),c=read();
if(0==opt){
add(l,r,c);
}else{
printf("%lld\n",query(l,r)%(c+1));
}
}
return 0;
}
6277. 数列分块入门 5
题面传送门
分块思路:区间开方,区间求和。
不完整的块直接暴力求即可。
完整的块区间怎么求呢。
根据题面ans<=2^31-1,我们考虑最坏情况,即区间是150000,求该区间和,那么每个位置最大约为2^16次方,
那么我们至多每个区间开方4次(向下取整),整个区间都变成0或者1.这些区间我们标记一下,下次对该块区间的开方就可以直接跳过。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)
#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 1000000000+7;
const int maxn = 1000;
using namespace std;
inline ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,k;
ll sum[50005],v[50005];
int b[50005];
bool flag[305];
inline void solve(int x){
if(flag[x]){
return;
}
flag[x]=true;
sum[x]=0;
for(int i=(x-1)*k+1;i<=x*k;++i){
v[i]=sqrt(v[i]);
sum[x]+=v[i];
if(v[i]>1){
flag[x]=false;
}
}
}
inline void add(int l,int r){
for(int i=l;i<=min(b[l]*k,r);++i){
sum[b[l]]-=v[i];
v[i]=sqrt(v[i]);
sum[b[l]]+=v[i];
}
if(b[l]!=b[r]){
for(int i=(b[r]-1)*k+1;i<=r;++i){
sum[b[r]]-=v[i];
v[i]=sqrt(v[i]);
sum[b[r]]+=v[i];
}
}
for(int i=b[l]+1;i<=b[r]-1;++i){
solve(i);
}
}
inline ll query(int l,int r){
int ans=0;
for(int i=l;i<=min(b[l]*k,r);++i){
ans+=v[i];
}
if(b[l]!=b[r]){
for(int i=(b[r]-1)*k+1;i<=r;++i){
ans+=v[i];
}
}
for(int i=b[l]+1;i<=b[r]-1;++i){
ans+=sum[i];
}
return ans;
}
int main(){
fin;
n=read(),k=sqrt(n);
for(int i=1;i<=n;++i){
v[i]=read();
}
for(int i=1;i<=n;++i){
b[i]=(i-1)/k+1;
sum[b[i]]+=v[i];
}
for(int i=1;i<=n;++i){
int opt=read(),l=read(),r=read(),c=read();
if(0==opt){
add(l,r);
}else{
printf("%lld\n",query(l,r));
}
}
}
6277. 数列分块入门 6
分块思路:mtag数组是块区间的乘法标记,atag数组是块区间的加法标记。
乘法:根据 (a+b)*c=a*c+b*c. a为乘法标记 b为加法标记
加法:直接加atag数组即可。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)
#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 10007;
const int maxn = 1000;
using namespace std;
inline ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,k;
int v[100005],mtag[1005],atag[1005],b[100005];
inline void reset(int x){
for(int i=(x-1)*k+1;i<=min(x*k,n);++i){
v[i]=(v[i]*mtag[x]+atag[x])%mod;
}
mtag[x]=1,atag[x]=0;
}
inline void solve(int f,int l,int r,int c){
reset(b[l]);
for(int i=l;i<=min(b[l]*k,r);++i){
if(0==f)v[i]+=c;
else v[i]*=c;
v[i]%=mod;
}
if(b[l]!=b[r]){
reset(b[r]);
for(int i=(b[r]-1)*k+1;i<=r;++i){
if(0==f)v[i]+=c;
else v[i]*=c;
v[i]%=mod;
}
}
for(int i=b[l]+1;i<=b[r]-1;++i){
if(0==f)atag[i]=(atag[i]+c)%mod;
else{
mtag[i]=(mtag[i]*c)%mod;
atag[i]=(atag[i]*c)%mod;
}
}
}
int main(){
n=read(),k=sqrt(n);
for(int i=1;i<=n;++i){
v[i]=read();
}
for(int i=1;i<=n;++i){
b[i]=(i-1)/k+1;
}
for(int i=1;i<=b[n];++i){
mtag[i]=1;
}
for(int i=1;i<=n;++i){
int opt=read(),l=read(),r=read(),c=read();
if(opt!=2){
solve(opt,l,r,c);
}else {
printf("%d\n",(v[r]*mtag[b[r]]+atag[b[r]])%mod);
}
}
return 0;
}
分块思路:题目涉及单点插入,单点查询。
那么我们就用一个可变的数组来存储块区间之间的值。
这里可以涉及重新分块的操作,虽然在这一题并不需要这样的操作,不过若是有区间查询的操作的话,
那么这个操作就比较有用了。
引用hzwer:
先说随机数据的情况
之前提到过,如果我们块内用数组以外的数据结构,能够支持其它不一样的操作,
比如此题每块内可以放一个动态的数组,每次插入时先找到位置所在的块,再暴力插入,把块内的其它元素直接向后移动一位,当然用链表也是可以的。
查询的时候类似,复杂度分析略。
但是这样做有个问题,如果数据不随机怎么办?
如果先在一个块有大量单点插入,这个块的大小会大大超过√n,那块内的暴力就没有复杂度保证了。
还需要引入一个操作:重新分块(重构)
每根号n次插入后,重新把数列平均分一下块,重构需要的复杂度为O(n),
重构的次数为√n,所以重构的复杂度没有问题,而且保证了每个块的大小相对均衡。
当然,也可以当某个块过大时重构,或者只把这个块分成两半。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)
#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 10007;
const int maxn = 1000;
using namespace std;
inline ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,k,m;
int v[100005];
int st[200005];
vector<int> vec[2005];
int tol=0;
inline pair<int,int> query(int l){
int x=1;
while(l>vec[x].size()){
l-=vec[x].size();
++x;
}
return make_pair(x,l-1);
}
inline void rebuild(){
int ed=0;
for(int i=1;i<=m;++i){
for(vector<int>::iterator it=vec[i].begin();it!=vec[i].end();++it){
st[++ed]=*it;
}
vec[i].clear();
}
int blo=sqrt(ed);
for(int i=1;i<=ed;++i){
vec[(i-1)/blo+1].push_back(st[i]);
}
m=(ed-1)/blo+1;
}
inline void insert(int a,int b){
++tol;
Pii x=query(a);
vec[x.first].insert(vec[x.first].begin()+x.second,b);
if(tol==k){ //每进行sqrt(n)插入操作进行一次重新分块
rebuild();
tol=0;
}
}
int main(){
fin;
n=read(),k=sqrt(n);
for(int i=1;i<=n;++i){
v[i]=read();
vec[(i-1)/k+1].push_back(v[i]);
}
m=(n-1)/k+1;
for(int i=1;i<=n;++i){
int op=read(),l=read(),r=read(),c=read();
if(0==op){
insert(l,r);
}else{
Pii t=query(r);
printf("%d\n",vec[t.first][t.second]);
}
}
return 0;
}