感觉这个不算科技吧,就是现在把操作降在块内了,算是暴力的优化
https://loj.ac/problem/6277
LOJ6277. 数列分块入门 1
对于这些数你有两种操作,0是区间+,1是区间查询
#include<bits/stdc++.h>
using namespace std;
#define N 50005
int m,belong[N];
int a[N],tag[N];
void change(int l,int r,int val)
{
for(int i=l;i<=min(r,belong[l]*m);i++) a[i]+=val;//将a块内剩余部分暴力更新
if(belong[l]!=belong[r])
{
for(int i=(belong[r]-1)*m+1;i<=r;i++) a[i]+=val;//将b块内剩余部分暴力更新
}
for(int i=belong[l]+1;i<=belong[r]-1;i++)tag[i]+=val;//将整块需要加的值更新上去
}
int main()
{
int n;
scanf("%d",&n);
m=sqrt(n);//一块有几个
for(int i=1;i<=n;i++) belong[i]=(i-1)/m+1;//将每个值分到所给的块内
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1,op,l,r,val;i<=n;i++)
{
scanf("%d%d%d%d",&op,&l,&r,&val);
if(!op)change(l,r,val);
else printf("%d\n",tag[belong[r]]+a[r]);//tag获取这个序列被加的值
}
return 0;
}
LOJ6278. 数列分块入门 2
这个还是区间加,但是要查询区间小于c*c的值
这个就麻烦多了,还要排序的啊
#include<bits/stdc++.h>
using namespace std;
#define N 50005
int n,m,belong[N];
int a[N],tag[N];
vector<int>vec[N];
void reset(int x)
{
//将两个特殊的块更新
vec[x].clear();
for(int i=(x-1)*m+1; i<=min(x*m,n); i++)vec[x].push_back(a[i]);
sort(vec[x].begin(),vec[x].end());
}
void change(int l,int r,int val)
{
for(int i=l; i<=min(r,belong[l]*m); i++) a[i]+=val; //将a块内剩余部分暴力更新
reset(belong[l]);
if(belong[l]!=belong[r])
{
for(int i=(belong[r]-1)*m+1; i<=r; i++) a[i]+=val; //将b块内剩余部分暴力更新
reset(belong[r]);
}
for(int i=belong[l]+1; i<=belong[r]-1; i++)tag[i]+=val; //将整块需要加的值更新上去
}
int query(int l,int r,int val)
{
int ans=0;
for(int i=l; i<=min(belong[l]*m,r); i++)if(a[i]+tag[belong[l]]<val)ans++;
if(belong[l]!=belong[r])
for(int i=(belong[r]-1)*m+1; i<=r; i++)if(a[i]+tag[belong[r]]<val)ans++;
for(int i=belong[l]+1; i<=belong[r]-1; i++)
ans+=lower_bound(vec[i].begin(),vec[i].end(),val-tag[i])-vec[i].begin();
return ans;
}
int main()
{
scanf("%d",&n);
m=sqrt(n);//一块有几个
for(int i=1; i<=n; i++)belong[i]=(i-1)/m+1; //将每个值分到所给的块内
for(int i=1; i<=n; i++)scanf("%d",&a[i]),vec[belong[i]].push_back(a[i]);
for(int i=1;i<=belong[n];i++)sort(vec[i].begin(),vec[i].end());//每块要先保证有序,因为以后不再更新了
for(int i=1,op,l,r,val; i<=n; i++)
{
scanf("%d%d%d%d",&op,&l,&r,&val);
if(!op)change(l,r,val);
else printf("%d\n",query(l,r,val*val));
}
return 0;
}
LOJ6279. 数列分块入门 3
询问区间内小于某个值 x的前驱(比其小的最大元素)
这个上面改改就行了
#include<bits/stdc++.h>
using namespace std;
#define N 100005
int n,m,belong[N];
int a[N],tag[N];
vector<int>vec[N];
void reset(int x)
{
//将两个特殊的块更新
vec[x].clear();
for(int i=(x-1)*m+1; i<=min(x*m,n); i++)vec[x].push_back(a[i]);
sort(vec[x].begin(),vec[x].end());
}
void change(int l,int r,int val)
{
for(int i=l; i<=min(r,belong[l]*m); i++) a[i]+=val; //将a块内剩余部分暴力更新
reset(belong[l]);
if(belong[l]!=belong[r])
{
for(int i=(belong[r]-1)*m+1; i<=r; i++) a[i]+=val; //将b块内剩余部分暴力更新
reset(belong[r]);
}
for(int i=belong[l]+1; i<=belong[r]-1; i++)tag[i]+=val; //将整块需要加的值更新上去
}
int query(int l,int r,int val)
{
int ans=-1;
for(int i=l; i<=min(belong[l]*m,r); i++)if(a[i]+tag[belong[l]]<val)ans=max(ans,a[i]+tag[belong[l]]);
if(belong[l]!=belong[r])
for(int i=(belong[r]-1)*m+1; i<=r; i++)if(a[i]+tag[belong[r]]<val)ans=max(ans,a[i]+tag[belong[r]]);
for(int i=belong[l]+1,pos; i<=belong[r]-1; i++)
{
pos=lower_bound(vec[i].begin(),vec[i].end(),val-tag[i])-vec[i].begin();
if(pos>=1) ans=max(ans,vec[i][pos-1]+tag[i]);
}
return ans;
}
int main()
{
scanf("%d",&n);
m=sqrt(n);//一块有几个
for(int i=1; i<=n; i++)belong[i]=(i-1)/m+1; //将每个值分到所给的块内
for(int i=1; i<=n; i++)scanf("%d",&a[i]),vec[belong[i]].push_back(a[i]);
for(int i=1;i<=belong[n];i++)sort(vec[i].begin(),vec[i].end());//每块要先保证有序,因为以后不更新了
for(int i=1,op,l,r,val; i<=n; i++)
{
scanf("%d%d%d%d",&op,&l,&r,&val);
if(!op)change(l,r,val);
else printf("%d\n",query(l,r,val));
}
return 0;
}
当然也可以用set去维护这个块,那个lowwerbound不要用错
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
#define N 100005
int n,m,belong[N];
int a[N],tag[N];
set<int>S[N];
void change(int l,int r,int val)
{
for(int i=l; i<=min(r,belong[l]*m); i++)
{
S[belong[l]].erase(a[i]);
a[i]+=val; //将a块内剩余部分暴力更新
S[belong[l]].insert(a[i]);
}
if(belong[l]!=belong[r])
{
for(int i=(belong[r]-1)*m+1; i<=r; i++)
{
S[belong[r]].erase(a[i]);
a[i]+=val; //将b块内剩余部分暴力更新
S[belong[r]].insert(a[i]);
}
}
for(int i=belong[l]+1; i<=belong[r]-1; i++)tag[i]+=val; //将整块需要加的值更新上去
}
int query(int l,int r,int val)
{
int ans=-1;
for(int i=l; i<=min(belong[l]*m,r); i++)if(a[i]+tag[belong[l]]<val)ans=max(ans,a[i]+tag[belong[l]]);
if(belong[l]!=belong[r])
for(int i=(belong[r]-1)*m+1; i<=r; i++)if(a[i]+tag[belong[r]]<val)ans=max(ans,a[i]+tag[belong[r]]);
for(int i=belong[l]+1; i<=belong[r]-1; i++)
{
set<int>::iterator it=S[i].lower_bound(val-tag[i]);//小于等于
if(it!=S[i].begin())ans=max(ans,*(--it)+tag[i]);
}
return ans;
}
int main()
{
scanf("%d",&n);
m=sqrt(n+0.5);//一块有几个
for(int i=1; i<=n; i++)belong[i]=(i-1)/m+1; //将每个值分到所给的块内
for(int i=1; i<=n; i++)scanf("%d",&a[i]),S[belong[i]].insert(a[i]);
for(int i=1,op,l,r,val; i<=n; i++)
{
scanf("%d%d%d%d",&op,&l,&r,&val);
if(!op)change(l,r,val);
else printf("%d\n",query(l,r,val));
}
return 0;
}
LOJ#6280. 数列分块入门 4
涉及区间求和,第一个代码xjb改改就好
#include<bits/stdc++.h>
using namespace std;
#define N 50005
#define ll long long
int m,belong[N];
ll a[N],sum[N],tag[N];
void change(int l,int r,int val)
{
for(int i=l;i<=min(r,belong[l]*m);i++) a[i]+=val,sum[belong[l]]+=val;//将a块内剩余部分暴力更新
if(belong[l]!=belong[r])
{
for(int i=(belong[r]-1)*m+1;i<=r;i++) a[i]+=val,sum[belong[r]]+=val;;//将b块内剩余部分暴力更新
}
for(int i=belong[l]+1;i<=belong[r]-1;i++)tag[i]+=val;//将整块需要加的值更新上去
}
ll query(int l,int r)//和上面思路差不多
{
ll ans=0;
for(int i=l;i<=min(r,belong[l]*m);i++)ans+=a[i]+tag[belong[l]];
if(belong[l]!=belong[r])
for(int i=(belong[r]-1)*m+1;i<=r;i++)ans+=a[i]+tag[belong[r]];
for(int i=belong[l]+1;i<=belong[r]-1;i++) ans+=sum[i]+m*tag[i];
return ans;
}
int main()
{
int n;
scanf("%d",&n);
m=sqrt(n+0.5);//一块有几个
for(int i=1;i<=n;i++) belong[i]=(i-1)/m+1;//将每个值分到所给的块内
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum[belong[i]]+=a[i];
for(int i=1,op,l,r,val;i<=n;i++)
{
scanf("%d%d%d%d",&op,&l,&r,&val);
if(!op)change(l,r,val);
else printf("%d\n",query(l,r)%(val+1));
}
return 0;
}
LOJ#6281. 数列分块入门 5
区间开方,区间求和,怎么那么像呢,这个不是暴力线段树更新?
当然分块也能做啊,上面的题目也可以线段树刷的
#include<bits/stdc++.h>
using namespace std;
#define N 50005
#define ll long long
int m,belong[N];
int a[N],sum[N],tag[N];
void la(int x)
{
if(tag[x])return;
tag[x]=1,sum[x]=0;
for(int i=(x-1)*m+1; i<=x*m; i++)
{
a[i]=sqrt(a[i]),sum[x]+=a[i];
if(a[i]>1) tag[x]=0;
}
}
void change(int l,int r)
{
for(int i=l; i<=min(r,belong[l]*m); i++) sum[belong[l]]-=a[i],a[i]=sqrt(a[i]),sum[belong[l]]+=a[i]; //将a块内剩余部分暴力更新
if(belong[l]!=belong[r])
{
for(int i=(belong[r]-1)*m+1; i<=r; i++) sum[belong[r]]-=a[i],a[i]=sqrt(a[i]),sum[belong[r]]+=a[i]; //将b块内剩余部分暴力更新
}
for(int i=belong[l]+1; i<=belong[r]-1; i++)la(i);//将整块需要加的值更新上去
}
int query(int l,int r)//和上面思路差不多
{
int ans=0;
for(int i=l; i<=min(r,belong[l]*m); i++)ans+=a[i];
if(belong[l]!=belong[r])
for(int i=(belong[r]-1)*m+1; i<=r; i++)ans+=a[i];
for(int i=belong[l]+1; i<=belong[r]-1; i++)ans+=sum[i];
return ans;
}
int main()
{
int n;
scanf("%d",&n);
m=sqrt(n+0.5);//一块有几个
for(int i=1; i<=n; i++) belong[i]=(i-1)/m+1; //将每个值分到所给的块内
for(int i=1; i<=n; i++) scanf("%d",&a[i]),sum[belong[i]]+=a[i];
for(int i=1,op,l,r,val; i<=n; i++)
{
scanf("%d%d%d%d",&op,&l,&r,&val);
if(!op)change(l,r);
else printf("%d\n",query(l,r));
}
return 0;
}
LOJ#6282. 数列分块入门 6
给出一个长为n的数列,以及n个操作,操作涉及单点插入,单点询问,数据随机生成。
vector的insert或者链表都可以吧,但是数据随机生成啊,就可以分块了
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define ll long long
int m,m1;
int a[N],tag[N];
vector<int>vec[N];
pair<int,int>query(int x)
{
int now=1;
while(x>vec[now].size())x-=vec[now].size(),now++;
return make_pair(now,x-1);
}
void rebuild()
{
int tot=0;
for(int i=1; i<=m1; i++)
{
for(auto it:vec[i])tag[++tot]=it;
vec[i].clear();
}
int mt=sqrt(tot);
for(int i=1; i<=tot; i++)
{
vec[(i-1)/mt+1].push_back(tag[i]);
}
m1=(tot-1)/mt+1;
}
void insert(int pos,int x)
{
pair<int,int>tmp=query(pos);
vec[tmp.first].insert(vec[tmp.first].begin()+tmp.second,x);
if(vec[tmp.first].size()>20*m)rebuild(); //重构
}
int main()
{
int n;
scanf("%d",&n);
m=sqrt(n+0.5);//一块有几个
for(int i=1; i<=n; i++) scanf("%d",&a[i]),vec[(i-1)/m+1].push_back(a[i]);
m1=(n-1)/m+1;
for(int i=1,op,l,r,val; i<=n; i++)
{
scanf("%d%d%d%d",&op,&l,&r,&val);
if(!op)insert(l,r);
else
{
pair<int,int>temp=query(r);
printf("%d\n",vec[temp.first][temp.second]);
}
}
return 0;
}
LOJ#6283. 数列分块入门 7
有一个区间乘法,还有区间加法,当然这个也可以去暴力更新
更新的时候要注意下,就是我乘起来的更新了,你乘的时候加法的也需要啊
#include<bits/stdc++.h>
using namespace std;
#define MD 10007
#define N 100005
#define ll long long
int m,n,belong[N];
int a[N],atag[N],mtag[N];
void reset(int x)
{
for(int i=(x-1)*m+1; i<=min(n,x*m); i++)
a[i]=(a[i]*mtag[x]+atag[x])%MD;
atag[x]=0,mtag[x]=1;
}
void add(int l,int r,int val)
{
reset(belong[l]);
for(int i=l; i<=min(r,belong[l]*m); i++)a[i]=(a[i]+val)%MD;
if(belong[l]!=belong[r])
{
reset(belong[r]);
for(int i=(belong[r]-1)*m+1; i<=r; i++)a[i]=(a[i]+val)%MD;
}
for(int i=belong[l]+1; i<=belong[r]-1; i++)atag[i]=(atag[i]+val)%MD;
}
void mul(int l,int r,int val)
{
reset(belong[l]);
for(int i=l; i<=min(r,belong[l]*m); i++)a[i]=(a[i]*val)%MD;
if(belong[l]!=belong[r])
{
reset(belong[r]);
for(int i=(belong[r]-1)*m+1; i<=r; i++)a[i]=(a[i]*val)%MD;
}
for(int i=belong[l]+1; i<=belong[r]-1; i++)atag[i]=(atag[i]*val)%MD,mtag[i]=(mtag[i]*val)%MD;
}
int main()
{
scanf("%d",&n);
m=sqrt(n+0.5);//一块有几个
for(int i=1; i<=n; i++) scanf("%d",&a[i]),belong[i]=(i-1)/m+1;
for(int i=1; i<=belong[n]; i++)mtag[i]=1;
for(int i=1,op,l,r,val; i<=n; i++)
{
scanf("%d%d%d%d",&op,&l,&r,&val);
if(op==0)add(l,r,val);
else if(op==1)mul(l,r,val);
else printf("%d\n",(a[r]*mtag[belong[r]]+atag[belong[r]])%MD);
}
return 0;
}
LOJ#6284. 数列分块入门 8
查询位于 [l,r]的数字有多少个是 c,再把位于 [l,r]的数字都改为 c
这个题目类似于第五个题目
每次都是修改区间,所以问题变得简单了起来
我每次修改一块的几个,那就是一块的复杂度,然后我只需要对这个块就行修改就好了
对于不完整块,只能把这个tag先更新一遍,再进行求解
tag==-1表示这个块的值不相同,如果改为c恰好是-1呢,所以这个值最好选一个没出现的值吧
所以我对分块有一点浅薄的认识,就是如果暴力来做复杂度很高,而且没有好的(或者说有你不会写)数据结构去维护,这个时候分块的话均摊的复杂度就不大,可以通过这种trick去搞这个题目
#include<bits/stdc++.h>
using namespace std;
#define MD 10007
#define N 100005
#define ll long long
int m,n,belong[N];
int a[N],tag[N];
void reset(int x)
{
if(tag[x]==-1)return;
for(int i=(x-1)*m+1; i<=min(n,x*m); i++)a[i]=tag[x];
tag[x]=-1;
}
int query(int l,int r,int val)
{
int ans=0;
reset(belong[l]);
for(int i=l; i<=min(r,belong[l]*m); i++)
{
if(a[i]!=val)a[i]=val;
else ans++;
}
if(belong[l]!=belong[r])
{
reset(belong[r]);
for(int i=(belong[r]-1)*m+1; i<=r; i++)
{
if(a[i]!=val)a[i]=val;
else ans++;
}
}
for(int i=belong[l]+1; i<=belong[r]-1; i++)
{
if(tag[i]!=-1)
{
if(tag[i]!=val) tag[i]=val;
else ans+=m;
}
else
{
for(int j=(i-1)*m+1; j<=i*m; j++)
{
if(a[j]!=val) a[j]=val;
else ans++;
}
tag[i]=val;
}
}
return ans;
}
int main()
{
scanf("%d",&n);
m=sqrt(n+0.5);//一块有几个
memset(tag,-1,sizeof tag);
for(int i=1; i<=n; i++) scanf("%d",&a[i]),belong[i]=(i-1)/m+1;
for(int i=1,l,r,val; i<=n; i++)
{
scanf("%d%d%d",&l,&r,&val);
printf("%d\n",query(l,r,val));
}
return 0;
}
LOJ#6285. 数列分块入门 9
(「BZOJ2724」[Violet 6] 蒲公英)
给出一个长为n的数列,以及n个操作,操作涉及询问区间的最小众数。
最后一个难爆了,表示根本想不出来
应该是完整的所有块的众数,和不完整块中出现的数。
所以我们可以预处理f(i,j)表示第 i 块到第 j 块的众数(枚举 i 开个桶扫一遍)。
那么只要能快速得出一个数在某个区间内出现次数即可,每次只要比较至多2√n+1个元素的出现次数,这题就解决了。
由于没有修改,只要离散化以后,给每个数 x 开个vector,按顺序存下 x 出现的位置,每次询问 x 时把区间的左右端点放进对应 vector 二分一下即可。
根据均值不等式,可以算出分块大小大概是√(n/logn)
抄黄学长的博客,迷迷糊糊算是看懂了吧,感觉好难
#include<bits/stdc++.h>
using namespace std;
int n,m,tot;
int v[50005],belong[50005];
int f[505][505];
map<int,int>M;
int val[50005],cnt[50005];
vector<int>vec[50005];
void pre(int x)
{
memset(cnt,0,sizeof cnt);
int mx=0,ans=0;
for(int i=(x-1)*m+1,t; i<=n; i++)
{
cnt[v[i]]++,t=belong[i];
if(cnt[v[i]]>mx||(cnt[v[i]]==mx&&val[v[i]]<val[ans]))ans=v[i],mx=cnt[v[i]];
f[x][t]=ans;
}
}
int query(int l,int r,int x)
{
int t=upper_bound(vec[x].begin(),vec[x].end(),r)-lower_bound(vec[x].begin(),vec[x].end(),l);
return t;
}
int query(int a,int b)
{
int ans,mx;
ans=f[belong[a]+1][belong[b]-1];
mx=query(a,b,ans);
for(int i=a,t; i<=min(belong[a]*m,b); i++)
{
t=query(a,b,v[i]);
if(t>mx||(t==mx&&val[v[i]]<val[ans]))ans=v[i],mx=t;
}
if(belong[a]!=belong[b])
for(int i=(belong[b]-1)*m+1,t; i<=b; i++)
{
t=query(a,b,v[i]);
if(t>mx||(t==mx&&val[v[i]]<val[ans]))ans=v[i],mx=t;
}
return ans;
}
int main()
{
scanf("%d",&n);
m=200;
//直接给定块大小,这个比较优秀,这个要根据均值不等式求得,在分块中,不同的块大小设置很影响程序的快慢
for(int i=1; i<=n; i++)
{
scanf("%d",&v[i]);
if(!M[v[i]])M[v[i]]=++tot,val[tot]=v[i];
v[i]=M[v[i]],vec[v[i]].push_back(i);
}
for(int i=1; i<=n; i++)belong[i]=(i-1)/m+1;
for(int i=1; i<=belong[n]; i++)pre(i);
for(int i=1,l,r; i<=n; i++)
{
scanf("%d%d",&l,&r);
printf("%d\n",val[query(l,r)]);
}
return 0;
}
来源:https://www.cnblogs.com/BobHuang/p/9632901.html