关于分块
·········
·········分块这个东西,码量比较小,维护东西还多,很灵活
主要来自LOJ的分块九题
区间修改,单点查询
维护懒标记,散块暴力修就是了
const int M=2e5+110;
inline int read(){
int sum=0,k=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();}
while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();}
return sum*k;
}
struct Line_Node{
int Next,To;
}Ed[M];
int Head[M],Tot=0;
int n,m,a[M];
int pos[M],bl;
struct fenkuai{
int lz,rz,lazy,sum;
}fk[M];
signed main(){
n=read();bl=sqrt(n);
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=bl;i++)
fk[i].lz=(i-1)*bl+1,
fk[i].rz=i*bl;
if(fk[bl].rz<n){
fk[++bl].lz=fk[bl-1].rz+1;
fk[bl].rz=n;
}
for(int i=1;i<=bl;i++)
for(int j=fk[i].lz;j<=fk[i].rz;j++)
pos[j]=i;
for(int chr=1;chr<=n;chr++){
int opt=read(),l=read(),r=read(),c=read();
if(opt==0){
int ql=pos[l],qr=pos[r];
if(ql==qr){
for(int i=l;i<=r;i++) a[i]+=c;
continue;
}
for(int i=l;i<=fk[ql].rz;i++) a[i]+=c;
for(int i=fk[qr].lz;i<=r;i++) a[i]+=c;
for(int i=ql+1;i<qr;i++)
fk[i].lazy+=c;
}
else{
cout<<a[r]+fk[pos[r]].lazy<<endl;
}
}
return 0;
}
很好的区间修改,区间小于数个数查询,
散块暴力加,暴力找,重构整块维护排序,整块维护lazy,查询时一遍二分找小于个数
注意:要copy一份原数组来做修改,一个数组会让散块矛盾
const int M=5e4+110;
inline int read(){
int sum=0,k=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();
}while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();
}return sum*k;
}
struct _fenkuai{
int lz,rz,lazy;
}fk[M];
int n,a[M],pos[M],bl; int a_[M];
inline void opt_0(int l,int r,int c){
int ql=pos[l],qr=pos[r];
if(ql==qr){
for(int i=l;i<=r;i++) a_[i]+=c;
for(int i=fk[ql].lz;i<=fk[ql].rz;i++) a[i]=a_[i];
sort(a+fk[ql].lz,a+fk[qr].rz+1);
return ;
}
for(int i=l;i<=fk[ql].rz;i++) a_[i]+=c;
for(int i=fk[ql].lz;i<=fk[ql].rz;i++) a[i]=a_[i];
sort(a+fk[ql].lz,a+1+fk[ql].rz);
for(int i=fk[qr].lz;i<=r;i++) a_[i]+=c;
for(int i=fk[qr].lz;i<=fk[qr].rz;i++) a[i]=a_[i];
sort(a+fk[qr].lz,a+1+fk[qr].rz);
for(int i=ql+1;i<qr;i++) fk[i].lazy+=c;
}
inline int fin(int l,int r,int c){
if(a[l]>=c) return 0;
if(a[r]<c) return r-l+1;
int ll=l,rr=r,mid,res=0;
while(ll<=rr) {
mid=(ll+rr)>>1;
if(a[mid]<c) res=mid-l+1,ll=mid+1;
else rr=mid-1;
}
return res;
}
inline void opt_1(int l,int r,int c){
c=c*c;
int ql=pos[l],qr=pos[r],cnt=0;
if(ql==qr){
//printf("%lld\n",fin(l,r,c-fk[ql].lazy));
for(int i=l;i<=r;i++) if(a_[i]+fk[ql].lazy<c) cnt++;
cout<<cnt<<"\n";
return ;
}
for(int i=l; i<=fk[ql].rz; i++)
if(a_[i] + fk[ql].lazy < c)
cnt++;
for(int i=fk[qr].lz; i<=r; i++)
if(a_[i] + fk[qr].lazy < c)
cnt++;
for(int i=ql+1;i<qr;i++)
cnt+=fin(fk[i].lz,fk[i].rz,c-fk[i].lazy);
printf("%lld\n",cnt);
return ;
}
signed main(){
n=read(),bl=sqrt(n); //块长
for(int i=1;i<=n;i++) a[i]=read(),a_[i]=a[i]; // a_ 是原版本
for(int i=1;i<=bl;i++) fk[i].lz=bl*(i-1)+1,fk[i].rz=bl*i;
if(fk[bl].rz<n) fk[bl].rz=n; //最后一块多一些
for(int i=1;i<=bl;i++){
for(int j=fk[i].lz;j<=fk[i].rz;j++) pos[j]=i;
sort(a+fk[i].lz,a+fk[i].rz+1);
}
for(int i=1;i<=n;i++){
int opt=read(),l=read(),r=read(),c=read();
if(opt==0) opt_0(l,r,c);
if(opt==1) opt_1(l,r,c);
}
return 0;
}
区间修改,区间查前驱
和分块二很像,散块暴力加,整块lazy,维护块内有序,查询时每个块做二分,比较一下就行了
做完前面两道,看到分块四,美味啊
区间加,区间查,维护lazy,秒了~
#define int long long
using namespace std;
const int M=3e5+110;
inline int read(){//快读
int sum=0,k=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();
}while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();
}return sum*k;
}
struct fenkuai{
int lz,rz,sum,lazy;
}fk[M];//分块
int Val[M],Pos[M],bl,m,n;
inline void Add(int x,int y,int k){
int ls=Pos[x],rs=Pos[y],res1=0,res2=0;//左右分块编号,零散数数量
if(ls==rs){//x,y在同一分块中
for(int i=x;i<=y;i++) Val[i]+=k;//暴力加
fk[ls].sum+=(y-x+1)*k;//更新sum
return ;
}
for(int i=x;i<=fk[ls].rz;i++) //处理左边散数
Val[i]+=k,res1++;
for(int i=fk[rs].lz;i<=y;i++) //处理右边散数
Val[i]+=k,res2++;
fk[ls].sum+=res1*k;//更新左sum
fk[rs].sum+=res2*k;//更新右sum
for(int i=ls+1;i<rs;i++){//处理中间分块
fk[i].lazy+=k;//懒标记
fk[i].sum+=(fk[i].rz-fk[i].lz+1)*k;//更新sum
}
return ;
}
inline void Ask(int x,int y,int z){
z+=1;
int ls=Pos[x],rs=Pos[y],res=0;
if(ls==rs){
for(int i=x;i<=y;i++) res=(res+Val[i]+fk[ls].lazy)%z;
printf("%lld\n",res%z);
return ;
}
for(int i=x;i<=fk[ls].rz;i++)
res=(res+Val[i]+fk[ls].lazy)%z;
for(int i=fk[rs].lz;i<=y;i++)
res=(res+Val[i]+fk[rs].lazy)%z;
for(int i=ls+1;i<rs;i++)
res=(res+fk[i].sum)%z;
printf("%lld\n",res%z);
return ;
}
signed main(){
n=read();bl=sqrt(n);
for(int i=1;i<=n;i++) Val[i]=read();
for(int i=1;i<=bl;i++){
fk[i].rz=i*bl;
fk[i].lz=(i-1)*bl+1;
}
if(fk[bl].rz<n){
fk[++bl].rz=n;
fk[bl].lz=fk[bl-1].rz+1;
}
for(int i=1;i<=bl;i++)
for(int j=fk[i].lz;j<=fk[i].rz;j++)
Pos[j]=i,fk[i].sum+=Val[j];
for(int chr=1;chr<=n;chr++){
int opt=read();
if(opt==0){
int x=read(),y=read(),v=read();
Add(x,y,v);
}
else{
int x=read(),y=read(),z=read();
Ask(x,y,z);
}
}
return 0;
}
区间开方,区间求和
注意到最大数值为 2的31次方减1,即2,147,483,648
这个东西开五次方就变成了1
暴力区间开方,如果一个区间全是1和0,打上标记即可
查询不变,和这道题一摸一样 P4145 上帝造题的七分钟 2 / 花神游历各国
单点插入,单点询问
维护vector分块,乱搞即可
#include<bits/stdc++.h>
using namespace std;
const int M=1e5+7;
inline int read(){
int sum=0,k=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();
}while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();
}return sum*k;
}
inline void wr(int x){
if(x<0) putchar('-'),x=-x;
if(x>9) wr(x/10);
putchar(x%10+'0');
return;
}
int n,bl,_a[M];
int Lz[M],Rz[M];
vector<int>a[M];
inline void opt_0(int l,int r){
for(int i=1;i<=bl;i++){
if(l<=a[i].size()){
auto pl=a[i].begin()+l-1;
a[i].insert(pl,r);
return ;
}
else l-=a[i].size();
}
}
inline int opt_1(int r){
for(int i=1;i<=bl;i++){
if(r<=a[i].size())
return *(a[i].begin()+r-1);
else r-=a[i].size();
}
}
signed main(){
n=read();bl=sqrt(n);
for(int i=1;i<=n;i++) _a[i]=read();
for(int i=1;i<=bl;i++) Lz[i]=Rz[i-1]+1,Rz[i]=i*bl;
Rz[bl]=n;
for(int i=1;i<=bl;i++)
for(int j=Lz[i];j<=Rz[i];j++)
a[i].push_back(_a[j]);
for(int i=1;i<=n;i++){
int opt=read(),l=read(),r=read(),c=read();
if(opt==0) opt_0(l,r);
if(opt==1) wr(opt_1(r)),printf("\n");
}
return 0;
}
区间乘法,区间加法,单点询问
很经典的数据结构,维护加lazy与乘lazy,修改时要将加lazy也乘上要乘的值
还有处理完散块要把lazy下放,reset块一下就好了
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int M=1e5+110,mod=10007;
inline int read(){
int sum=0,k=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();
}while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();
}return sum*k;
}
int Head[M],Tot=0;
struct _Node{
int Next,To;
}Ed[M];
inline void Adde(int u,int v){
Ed[++Tot]={Head[u],v};
Head[u]=Tot;
}
struct _fenkuai{
int lz=0,rz=0;
int lazy_add=0;
int lazy_mul=1;
}fk[M];
int pos[M],bl,n,a[M];
inline void reset(int pl){
for(int i=fk[pl].lz;i<=fk[pl].rz;i++){
a[i]=(a[i]*fk[pl].lazy_mul+fk[pl].lazy_add)%mod;
}fk[pl].lazy_mul=1,fk[pl].lazy_add=0;
return ;
}
inline void opt_0(int l,int r,int c){
int ql=pos[l],qr=pos[r];
if(ql==qr){
reset(ql);
for(int i=l;i<=r;i++)
a[i]+=c,a[i]%=mod;
return ;
}
reset(ql),reset(qr);
for(int i=l;i<=fk[ql].rz;i++) a[i]+=c,a[i]%=mod;
for(int i=fk[qr].lz;i<=r;i++) a[i]+=c,a[i]%=mod;
for(int i=ql+1;i<qr;i++) fk[i].lazy_add+=c,fk[i].lazy_add%=mod;
}
inline void opt_1(int l,int r,int c){
int ql=pos[l],qr=pos[r];
if(ql==qr){
reset(ql);
for(int i=l;i<=r;i++)
a[i]*=c,a[i]%=mod;
return ;
}
reset(ql),reset(qr);
for(int i=l;i<=fk[ql].rz;i++) a[i]*=c,a[i]%=mod;
for(int i=fk[qr].lz;i<=r;i++) a[i]*=c,a[i]%=mod;
for(int i=ql+1;i<qr;i++) fk[i].lazy_add*=c,fk[i].lazy_add%=mod,fk[i].lazy_mul*=c,fk[i].lazy_mul%=mod;
}
inline void opt_2(int r){
int pl=pos[r];
int res=fk[pl].lazy_add+fk[pl].lazy_mul*a[r];
printf("%lld\n",res%mod);
return;
}
signed main(){
n=read();bl=sqrt(n);
for(int i=1;i<=n;i++) a[i]=read(),a[i]%=mod;
for(int i=1;i<=bl;i++)fk[i].lz=bl*(i-1)+1,fk[i].rz=bl*i;
if(fk[bl].rz<n) fk[++bl].rz=n,fk[bl].lz=fk[bl-1].rz+1;
for(int i=1;i<=bl;i++)
for(int j=fk[i].lz;j<=fk[i].rz;j++)
pos[j]=i;
for(int i=1;i<=n;i++){
int opt=read(),l=read(),r=read(),c=read();
if(opt==0) opt_0(l,r,c);
if(opt==1) opt_1(l,r,c);
if(opt==2) opt_2(r);
}
return 0;
}
区间推平,区间颜色查询,没写分块,珂朵莉树水过了(😓)
静态区间最小众数双倍经验P4168 [Violet] 蒲公英
只会88pts,最后几个卡不过去qmq
维护一个离散化,用map<int,int>记录数的出现次数(桶)
维护一个s数组,s[i][j]表示前i个块中数字j出现了几次(前缀和)
维护p数组, p[i][j]第[i,j]块中最小的众数
最后整块有p数组直接找,散块用s数组暴力查
88pts
#include<bits/stdc++.h>
using namespace std;
const int M=320;
inline int read(){
int sum=0,k=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();
}while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();
}return sum*k;
}
inline void wr(int x){
if(x<0) putchar('-'),x=-1;
if(x>9) wr(x/10);
putchar(x%10+'0');
}
struct fenkuai{
int lz,rz;
}fk[M*M];
int n,bl,m,a[M*M],pos[M*M],p[M][M],s[M][M*M];
vector<int>Li;//离散化~
map<int,int>mapp;//记录数字出现次数
inline int ge(int x){//离散得值
return lower_bound(Li.begin(),Li.end(),x)-Li.begin()+1;
}
inline int ask(int l,int r){//l~r的区间最小众数
mapp.clear();//清空
int ql=pos[l],qr=pos[r],res=0,maxx=-1;//l,r属于的分块
if(ql==qr){//l,r在同一块中
for(int i=l;i<=r;i++){//暴力找
int val=ge(a[i]),num=++mapp[val];
if((num>maxx)||(num==maxx&&val<res))
maxx=num,res=val;//更新
}
return Li[res-1];
}
for(int i=l;i<=fk[ql].rz;i++) mapp[ge(a[i])]++;//散块
for(int i=fk[qr].lz;i<=r;i++) mapp[ge(a[i])]++;//散块
res=p[ql+1][qr-1],maxx=mapp[res]+s[qr-1][res]-s[ql][res];//整块的众数
for(auto sur:mapp){//遍历散块可能众数
int val=sur.first,num=sur.second,ch=num+s[qr-1][val]-s[ql][val];
if((ch>maxx)||(ch==maxx&&val<res))
maxx=ch,res=val;//更新~
}return Li[res-1];
}
signed main(){
n=read();bl=sqrt(n);
for(int i=1;i<=n;i++) a[i]=read(),Li.push_back(a[i]);
for(int i=1;i<=bl;i++) fk[i].lz=fk[i-1].rz+1,fk[i].rz=i*bl;
fk[bl].rz=n;
for(int i=1;i<=bl;i++) for(int j=fk[i].lz;j<=fk[i].rz;j++) pos[j]=i;
sort(Li.begin(),Li.end());
Li.erase(std::unique(Li.begin(),Li.end()),Li.end());
m=Li.size();
for(int i=1;i<=bl;i++){
mapp.clear();
int res,maxx=-1;
for(int j=i;j<=bl;j++){
for(int k=fk[j].lz;k<=fk[j].rz;k++){
int val=ge(a[k]);mapp[val]++;
if((mapp[val]>maxx)||(mapp[val]==maxx&&val<res))
res=val,maxx=mapp[val];
}
p[i][j]=res;
}
}
mapp.clear();
for(int i=1;i<=bl;i++){
for(int j=fk[i].lz;j<=fk[i].rz;j++) mapp[ge(a[j])]++;
for(int j=1;j<=m;j++) s[i][j]=mapp[j];
}
for(int i=1;i<=n;i++){
int l=read(),r=read();
wr(ask(l,r));putchar('\n');
}
return 0;
}
すべての生命よ,歌のように輝いています||截剣式、斬、断、破です!||雲璃猫猫が好きですqwq