Loj分块入门1-9

参考题目来源、hzwer学长的题解链接

目录

    • 1.区间加法、单点查值
    • 2.区间加法、询问区间内小于变量c的元素个数
    • 3.区间加法、询问区间内小于变量c的前驱(比其小的最大元素)
    • 4.区间加法,区间求和
    • 5.区间开方、区间求和
    • 6.单点插入,单点询问
    • 7.区间加法、区间乘法、单点查值
    • 8.区间询问等于一个数c的元素,并将这个区间的所有元素改为c
    • 9.询问区间最小众数

1.区间加法、单点查值

用lazy数组,简单

#include
using namespace std;
typedef long long ll;
#define maxn 50100
int n,l[maxn],r[maxn],belong[maxn],size,cnt;
ll v[maxn],a[maxn],lazy[maxn];

void build(){
	size=sqrt(n);
	cnt=n/size;if(n%size)cnt++;
	for(int i=1;i<=cnt;i++)
		l[i]=(i-1)*size+1,r[i]=i*size;
	for(int i=1;i<=n;i++)
		belong[i]=(i-1)/size+1;
}
void updata(int l_,int r_,ll val){
	if(belong[l_]==belong[r_]){
		for(int i=l_;i<=r_;i++)a[i]+=val;
		return;
	}
	for(int i=l_;i<=r[belong[l_]];i++)a[i]+=val;
	for(int i=belong[l_]+1;i<belong[r_];i++)lazy[i]+=val;
	for(int i=l[belong[r_]];i<=r_;i++)a[i]+=val;
}
ll query(int x){
	return a[x]+lazy[belong[x]];
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	build();
	int op,l,r,c;
	for(int i=1;i<=n;i++){
		scanf("%d%d%d%d",&op,&l,&r,&c);
		if(op==0)updata(l,r,c);
		else printf("%lld\n",query(r));
	}
}

2.区间加法、询问区间内小于变量c的元素个数

用lazy数组,非整体块暴力,整体块内排序+二分(vector)

#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=50007;
template<class T>inline void read(T &x){
	x=0;int f=0;char ch=getchar();
	while(ch<'0'||ch>'9'){f|=ch=='-';ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=f?-x:x;
	return;
}
int n,size,cnt,l[N],r[N],belong[N];
int a[N],lazy[N];
vector<int>vec[505];
void build(){
	size=sqrt(n);
	cnt=n/size;if(n%size)cnt++;
	for(int i=1;i<=cnt;i++)
		l[i]=(i-1)*size+1,r[i]=i*size;
	r[cnt]=n;
	for(int i=1;i<=n;i++){
		belong[i]=(i-1)/size+1;
		vec[belong[i]].push_back(a[i]);
	}
	for(int i=1;i<=cnt;i++)
		sort(vec[i].begin(),vec[i].end());
}
void reset(int x){
	vec[x].clear();
	for(int i=l[x];i<=r[x];i++)
		vec[x].push_back(a[i]);
	sort(vec[x].begin(),vec[x].end());
}
void add(int L,int R,int c){
	if(belong[L]==belong[R]){
		for(int i=L;i<=R;i++)a[i]+=c;
		reset(belong[L]);
		return;
	}
	for(int i=L;i<=r[belong[L]];i++)a[i]+=c;
	reset(belong[L]);
	for(int i=belong[L]+1;i<belong[R];i++)lazy[i]+=c;
	for(int i=l[belong[R]];i<=R;i++)a[i]+=c;
	reset(belong[R]);
}
int query(int L,int R,int c){
	int ans=0;
	if(belong[L]==belong[R]){
		for(int i=L;i<=R;i++)
			if(a[i]+lazy[belong[i]]<c)ans++;
		return ans;
	}
	for(int i=L;i<=r[belong[L]];i++)
		if(a[i]+lazy[belong[i]]<c)ans++;
	for(int i=belong[L]+1;i<belong[R];i++){
		int x=c-lazy[i];
		ans+=lower_bound(vec[i].begin(),vec[i].end(),x)-vec[i].begin();
	}
	for(int i=l[belong[R]];i<=R;i++)
		if(a[i]+lazy[belong[i]]<c)ans++;
	return ans;
}	
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)read(a[i]);
	build();
	int opt,l,r,c;
	for(int i=1;i<=n;i++){
		read(opt);read(l);read(r);read(c);
		if(opt==0)add(l,r,c);
		else printf("%d\n",query(l,r,c*c));
	}
}

3.区间加法、询问区间内小于变量c的前驱(比其小的最大元素)

用lazy数组,非整体块暴力,整体块内排序+二分(vector)

#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=100007;
template<class T>inline void read(T &x){
	x=0;int f=0;char ch=getchar();
	while(ch<'0'||ch>'9'){f|=ch=='-';ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=f?-x:x;
	return;
}
int n,size,cnt,l[N],r[N],belong[N];
int a[N],lazy[N];
vector<int>vec[505];//块内排序 
void build(){
	size=sqrt(n);
	cnt=n/size;if(n%size)cnt++;
	for(int i=1;i<=cnt;i++)
		l[i]=(i-1)*size+1,r[i]=i*size;
	r[cnt]=n;
	for(int i=1;i<=n;i++){
		belong[i]=(i-1)/size+1;
		vec[belong[i]].push_back(a[i]);
	}
	for(int i=1;i<=cnt;i++)
		sort(vec[i].begin(),vec[i].end());
}
void reset(int x){//重新排序 
	vec[x].clear();
	for(int i=l[x];i<=r[x];i++)
		vec[x].push_back(a[i]);
	sort(vec[x].begin(),vec[x].end());
}
void add(int L,int R,int c){
	if(belong[L]==belong[R]){
		for(int i=L;i<=R;i++)a[i]+=c;
		reset(belong[L]);
		return;
	}
	for(int i=L;i<=r[belong[L]];i++)a[i]+=c;
	reset(belong[L]);
	for(int i=belong[L]+1;i<belong[R];i++)lazy[i]+=c;
	for(int i=l[belong[R]];i<=R;i++)a[i]+=c;
	reset(belong[R]);
}
int query(int L,int R,int c){
	int pre=-1;
	if(belong[L]==belong[R]){
		for(int i=L;i<=R;i++)
			if(a[i]+lazy[belong[i]]<c&&a[i]+lazy[belong[i]]>pre)pre=a[i]+lazy[belong[i]];
		return pre;
	}
	for(int i=L;i<=r[belong[L]];i++)
		if(a[i]+lazy[belong[i]]<c&&a[i]+lazy[belong[i]]>pre)pre=a[i]+lazy[belong[i]];
	for(int i=belong[L]+1;i<belong[R];i++){
		int x=c-lazy[i];
		int p=lower_bound(vec[i].begin(),vec[i].end(),x)-vec[i].begin();
		if(p==0)continue;
		if(vec[i][p-1]+lazy[i]>pre)pre=vec[i][p-1]+lazy[i];
	}
	for(int i=l[belong[R]];i<=R;i++)
		if(a[i]+lazy[belong[i]]<c&&a[i]+lazy[belong[i]]>pre)pre=a[i]+lazy[belong[i]];
	return pre;
}	
int main(){
//	freopen("a1.in","r",stdin);
	cin>>n;
	for(int i=1;i<=n;i++)read(a[i]);
	build();
	int opt,l,r,c;
	for(int i=1;i<=n;i++){
		read(opt);read(l);read(r);read(c);
		if(opt==0)add(l,r,c);
		else printf("%d\n",query(l,r,c));
	}
//	fclose(stdin);
}

4.区间加法,区间求和

lazy数组

#include
using namespace std;
typedef long long ll;
const int N=50007;
int n,m,size,cnt,l[N],r[N],belong[N];
ll v[N],a[N],lazy[N];
void build(){
	size=sqrt(n);
	cnt=n/size;if(n%size)cnt++;
	for(int i=1;i<=cnt;i++)
		l[i]=(i-1)*size+1,r[i]=i*size;
	r[cnt]=n;
	for(int i=1;i<=n;i++)belong[i]=(i-1)/size+1;
	for(int i=1;i<=cnt;i++)
		for(int j=l[i];j<=r[i];j++)
			v[i]+=a[j];
}

inline void add(int L,int R,ll c){
	if(belong[L]==belong[R]){
		for(int i=L;i<=R;i++)a[i]+=c,v[belong[i]]+=c;
		return;
	}
	for(int i=L;i<=r[belong[L]];i++)a[i]+=c,v[belong[i]]+=c;
	for(int i=belong[L]+1;i<belong[R];i++)lazy[i]+=c;
	for(int i=l[belong[R]];i<=R;i++)a[i]+=c,v[belong[i]]+=c;
}
inline ll query(int L,int R,ll c){
	ll ans=0;
	if(belong[L]==belong[R]){
		for(int i=L;i<=R;i++)ans=(ans+a[i]+lazy[belong[i]])%c;
		return ans;
	}
	for(int i=L;i<=r[belong[L]];i++)ans=(ans+a[i]+lazy[belong[i]])%c;
	for(int i=belong[L]+1;i<belong[R];i++)ans=(ans+v[i]+lazy[i]*(r[i]-l[i]+1))%c;
	for(int i=l[belong[R]];i<=R;i++)ans=(ans+a[i]+lazy[belong[i]])%c;
	return ans;
}

int main(){
	//freopen("a1.in","r",stdin);
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	build();
	int opt,l_,r_;ll c;
	for(int i=1;i<=n;i++){
		cin>>opt>>l_>>r_>>c;
		if(opt==0)add(l_,r_,c);
		else{
			ll ans=query(l_,r_,c+1);
			cout<<ans<<"\n";
		}
	}
	//fclose(stdin);
}

5.区间开方、区间求和

int类型最多开方5次(向下取整)会变成1/0,就没有必要再开方了
非整体块暴力,整体块也暴力开方,用flag记录整体块内是否都已为1/0,(若是则直接累加,不需要再暴力块内元素了)
这样每个元素至多被开方不超过5次,显然复杂度没有问题。

#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define inf 0x7fffffff
typedef long long ll;
const int N=50007;
int n,size,cnt,l[N],r[N],belong[N];
int a[N],v[N],flag[N];//flag判断块是否已为0/1 
void build(){
	size=sqrt(n);
	cnt=n/size;if(n%size)cnt++;
	for(int i=1;i<=cnt;i++)
		l[i]=(i-1)*size+1,r[i]=i*size;
	r[cnt]=n;
	for(int i=1;i<=n;i++)belong[i]=(i-1)/size+1;
	for(int i=1;i<=cnt;i++)
		for(int j=l[i];j<=r[i];j++)
			v[i]+=a[j];
}
void solve_sqrt(int x){//暴力对整体块开方
	if(flag[x])return;
	flag[x]=1;
	v[x]=0;
	for(int i=l[x];i<=r[x];i++){
		a[i]=sqrt(a[i]);v[x]+=a[i];
		if(a[i]>1)flag[x]=0;
	}
}
void updata_sqrt(int L,int R){
	if(belong[L]==belong[R]){
		for(int i=L;i<=R;i++){
			v[belong[i]]-=a[i];
			a[i]=sqrt(a[i]);
			v[belong[i]]+=a[i];
		}
		return;
	}
	for(int i=L;i<=r[belong[L]];i++){
		v[belong[i]]-=a[i];
		a[i]=sqrt(a[i]);
		v[belong[i]]+=a[i];
	}
	for(int i=belong[L]+1;i<belong[R];i++)
		solve_sqrt(i);
	for(int i=l[belong[R]];i<=R;i++){
		v[belong[i]]-=a[i];
		a[i]=sqrt(a[i]);
		v[belong[i]]+=a[i];
	}
}
int query(int L,int R){
	int ans=0;
	if(belong[L]==belong[R]){
		for(int i=L;i<=R;i++)ans+=a[i];
		return ans;
	}
	for(int i=L;i<=r[belong[L]];i++)ans+=a[i];
	for(int i=belong[L]+1;i<belong[R];i++)ans+=v[i];
	for(int i=l[belong[R]];i<=R;i++)ans+=a[i];
	return ans;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	build();
	int opt,l_,r_,c;
	for(int i=1;i<=n;i++){
		scanf("%d%d%d%d",&opt,&l_,&r_,&c);
		if(opt==0)updata_sqrt(l_,r_);
		else printf("%d\n",query(l_,r_));
	}
}

6.单点插入,单点询问

用vector的函数暴力插入,当某个块过大时重新分块(rebuild)
询问的时候找第k个元素在第几个块的哪个位置

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define Inf 0x7fffffff
typedef long long ll;
const int N=100007;
int size,cnt,belong[N],l[N],r[N];
int n,a[N],temp[N<<1];
vector<int>ve[1005];
template<class T>inline void read(T &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9')  {f|=(ch=='-');ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x=f?-x:x;
    return;
}
pair<int,int>find(int k){//找第k个元素在第几个块内的哪个位置
	int i=1;
	while(k>ve[i].size())k-=ve[i].size(),i++;
	return make_pair(i,k-1);
}
void rebuild(){//重新分块
	int k=0;
	for(int i=1;i<=cnt;i++){
		for(vector<int>::iterator j=ve[i].begin();j!=ve[i].end();j++)
			temp[++k]=*j; //*j是取迭代器内的元素 
		ve[i].clear();
	}
	int size2=sqrt(k),cnt2=(k-1)/size2+1;
	for(int i=1;i<=k;i++)ve[(i-1)/size2+1].push_back(temp[i]);
	cnt=(k-1)/size2+1;
}	
void insert(int pos,int x){
	pair<int,int>t=find(pos);
	ve[t.first].insert(ve[t.first].begin()+t.second,x);
	if(ve[t.first].size()>20*size)rebuild();
}

int main(){
	cin>>n;
	for(int i=1;i<=n;i++)read(a[i]);
	size=sqrt(n);cnt=(n-1)/size+1;
	for(int i=1;i<=n;i++)ve[(i-1)/size+1].push_back(a[i]);
	int opt,l_,r_,c;
	for(int i=1;i<=n;i++){
		read(opt);read(l_);read(r_);read(c);
		if(opt==0)insert(l_,r_);
		else{
			pair<int,int>t=find(r_);
			printf("%d\n",ve[t.first][t.second]);
		}
	}
}

7.区间加法、区间乘法、单点查值

lazy加法懒标记,lazy2乘法懒标记
让乘法标记的优先级高于加法(如果反过来的话,新的加法标记无法处理)
1.若当前的一个块乘以lazy2后加上lazy,这时进行一个乘c的操作,则原来的标记变成lazy2 *c,lazy *c
2.若当前的一个块乘以lazy2后加上lazy,这时进行一个加c的操作,则原来的标记变成lazy2, lazy +c
重点:每次更新非整体块时用reset下放标记

#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define inf 0x7fffffff
typedef long long ll;
const int N=100007,mod=10007;
int n,size,cnt,l[N],r[N],belong[N];
int a[N],lazy[N],lazy2[N];//lazy是加法标记,lazy2是乘法标记
template<class T>inline void read(T &x){
	x=0;int f=0;char ch=getchar();
	while(ch<'0'||ch>'9'){f|=ch=='-';ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=f?-x:x;
	return;
}
void build(){
	size=sqrt(n);
	cnt=n/size;if(n%size)cnt++;
	for(int i=1;i<=cnt;i++)
		l[i]=(i-1)*size+1,r[i]=i*size;
	r[cnt]=n;
	for(int i=1;i<=n;i++)belong[i]=(i-1)/size+1;
	for(int i=1;i<=cnt;i++)lazy2[i]=1;
}
void reset(int x){
	for(int i=l[x];i<=r[x];i++)
		a[i]=(a[i]*lazy2[x]+lazy[x])%mod;
	lazy[x]=0;lazy2[x]=1;
}
void add(int L,int R,int c){
	if(belong[L]==belong[R]){
		reset(belong[L]);
		for(int i=L;i<=R;i++)a[i]=(a[i]+c)%mod;
		return;
	}
	reset(belong[L]);
	for(int i=L;i<=r[belong[L]];i++)a[i]=(a[i]+c)%mod;
	for(int i=belong[L]+1;i<belong[R];i++)lazy[i]=(lazy[i]+c)%mod;
	reset(belong[R]);
	for(int i=l[belong[R]];i<=R;i++)a[i]=(a[i]+c)%mod;
}
void muti(int L,int R,int c){
	if(belong[L]==belong[R]){
		reset(belong[L]);
		for(int i=L;i<=R;i++)a[i]=(a[i]*c)%mod;
		return;
	}
	reset(belong[L]);
	for(int i=L;i<=r[belong[L]];i++)a[i]=(a[i]*c)%mod;
	for(int i=belong[L]+1;i<belong[R];i++){
		lazy[i]=(lazy[i]*c)%mod;
		lazy2[i]=(lazy2[i]*c)%mod;
	}
	reset(belong[R]);
	for(int i=l[belong[R]];i<=R;i++)a[i]=(a[i]*c)%mod;
}
int query(int p){
	return (a[p]*lazy2[belong[p]]+lazy[belong[p]])%mod;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)read(a[i]);
	build();
	int opt,l_,r_,c;
	for(int i=1;i<=n;i++){
		read(opt);read(l_);read(r_);read(c);
		if(opt==0)add(l_,r_,c);else
		if(opt==1)muti(l_,r_,c);else
		printf("%d\n",query(r_));
	}
}		

8.区间询问等于一个数c的元素,并将这个区间的所有元素改为c

非整体块暴力修改,整体块也暴力修改,用flag记录整体块是否修改为了同一元素,(若是则直接累加,不需要再暴力块内元素了)
重点:暴力非整体块时用reset下放整体块修改的值

#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define inf 0x7fffffff
typedef long long ll;
const int N=100007;
int n,size,cnt,l[N],r[N],belong[N];
int a[N],flag[N],v[N];
template<class T>void read(T &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9')  {f|=(ch=='-');ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x=f?-x:x;
    return;
}
void build(){
	size=sqrt(n);
	cnt=n/size;if(n%size)cnt++;
	for(int i=1;i<=cnt;i++)
		l[i]=(i-1)*size+1,r[i]=i*size;
	r[cnt]=n;
	for(int i=1;i<=n;i++)
		belong[i]=(i-1)/size+1;
}
void reset(int x){
	if(!flag[x])return;
	for(int i=l[x];i<=r[x];i++)a[i]=v[x];
	flag[x]=0;
}
int query(int L,int R,int c){
	int ans=0;
	if(belong[L]==belong[R]){
		reset(belong[L]);
		for(int i=L;i<=R;i++)
			if(a[i]==c)ans++;
			else a[i]=c;
		return ans;
	}
	reset(belong[L]);
	for(int i=L;i<=r[belong[L]];i++)
		if(a[i]==c)ans++;
		else a[i]=c;
	for(int i=belong[L]+1;i<belong[R];i++){
		if(flag[i]){
			if(v[i]==c)ans+=r[i]-l[i]+1;
			else v[i]=c;
		}else{
			flag[i]=1;
			for(int j=l[i];j<=r[i];j++){
				if(a[j]==c)ans++;
				else a[j]=c;
			}
			v[i]=c;
		}
	}
	reset(belong[R]);
	for(int i=l[belong[R]];i<=R;i++)
		if(a[i]==c)ans++;
		else a[i]=c;
	return ans;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)read(a[i]);
	build();
	int l_,r_,c;
	for(int i=1;i<=n;i++){
		read(l_);read(r_);read(c);
		printf("%d\n",query(l_,r_,c));
	}
}	

9.询问区间最小众数

Ovo 这怎么查

你可能感兴趣的:(Algorithm,and,Data,structure)