LOJ 数列分块入门系列

目录

1.区间加+单点查
每个块维护tag,散的暴力改。
code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=50010;
const int maxt=250;
int n,t,cnt;
int a[maxn],tag[maxt],L[maxt],R[maxt],pos[maxn];
inline void add(int ql,int qr,int k)
{
	if(pos[ql]==pos[qr])
	{
		for(int i=ql;i<=qr;i++)a[i]+=k;
		return;
	}
	for(int i=ql;i<=R[pos[ql]];i++)a[i]+=k;
	for(int i=L[pos[qr]];i<=qr;i++)a[i]+=k;
	if(pos[ql]+1<=pos[qr]-1)for(int i=pos[ql]+1;i<=pos[qr]-1;i++)tag[i]+=k;
}
int main()
{
	scanf("%d",&n);
	t=sqrt(n);cnt=n/t;
	if(n%t)cnt++;
	for(int i=1;i<=cnt;i++)L[i]=(i-1)*t+1,R[i]=min(i*t,n);
	for(int i=1;i<=n;i++)pos[i]=(i-1)/t+1;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		int op,x,y,k;
		scanf("%d%d%d%d",&op,&x,&y,&k);
		if(!op)add(x,y,k);
		else printf("%d
",a[y]+tag[pos[y]]);
	}
	return 0;
}

2.区间加+查询区间小于某个数的数的个数
这题类似。
code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=50010;
const int maxt=250;
int n,t,cnt;
int a[maxn],b[maxn],L[maxt],R[maxt],pos[maxn],tag[maxt];
inline void add(int ql,int qr,int k)
{
	if(pos[ql]==pos[qr])
	{
		for(int i=ql;i<=qr;i++)a[i]+=k;
		for(int i=L[pos[ql]];i<=R[pos[qr]];i++)b[i]=a[i];
		sort(b+L[pos[ql]],b+R[pos[qr]]+1);
		return;
	}
	for(int i=ql;i<=R[pos[ql]];i++)a[i]+=k;
	for(int i=L[pos[ql]];i<=R[pos[ql]];i++)b[i]=a[i];
	sort(b+L[pos[ql]],b+R[pos[ql]]+1);
	for(int i=L[pos[qr]];i<=qr;i++)a[i]+=k;
	for(int i=L[pos[qr]];i<=R[pos[qr]];i++)b[i]=a[i];
	sort(b+L[pos[qr]],b+R[pos[qr]]+1);
	if(pos[ql]+1<=pos[qr]-1)for(int i=pos[ql]+1;i<=pos[qr]-1;i++)tag[i]+=k;
}
inline int query(int ql,int qr,int k)
{
	int res=0;
	if(pos[ql]==pos[qr])
	{
		for(int i=ql;i<=qr;i++)if(a[i]+tag[pos[ql]]<k)res++;
		return res;
	}
	for(int i=ql;i<=R[pos[ql]];i++)if(a[i]+tag[pos[ql]]<k)res++;
	for(int i=L[pos[qr]];i<=qr;i++)if(a[i]+tag[pos[qr]]<k)res++;
	if(pos[ql]+1<=pos[qr]-1)
		for(int i=pos[ql]+1;i<=pos[qr]-1;i++)
		{
			int l=L[i],r=R[i],ans=0;
			while(l<=r)
			{
				int mid=(l+r)>>1;
				if(b[mid]+tag[i]<k)l=mid+1,ans=mid-L[i]+1;
				else r=mid-1;
			}
			res+=ans;
		}
	return res;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
	t=sqrt(n);cnt=n/t;
	if(n%t)cnt++;
	for(int i=1;i<=cnt;i++)L[i]=(i-1)*t+1,R[i]=min(i*t,n);
	for(int i=1;i<=n;i++)pos[i]=(i-1)/t+1;
	for(int i=1;i<=cnt;i++)sort(b+L[i],b+R[i]+1);
	for(int i=1;i<=n;i++)
	{
		int op,l,r,k;scanf("%d%d%d%d",&op,&l,&r,&k);
		if(!op)add(l,r,k);
		else printf("%d
",query(l,r,k*k));
	}
	return 0;
}

3.区间加+区间查前驱
每个块内放个multiset,查询时lower_bound
code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
const int maxt=350;
int n,t,cnt;
int a[maxn],L[maxt],R[maxt],pos[maxn],tag[maxt];
multiset<int>s[maxt];
inline void add(int ql,int qr,int k)
{
	if(pos[ql]==pos[qr])
	{
		for(int i=ql;i<=qr;i++)s[pos[ql]].erase(s[pos[ql]].find(a[i]));
		for(int i=ql;i<=qr;i++)a[i]+=k;
		for(int i=ql;i<=qr;i++)s[pos[ql]].insert(a[i]);
		return;
	}
	for(int i=ql;i<=R[pos[ql]];i++)s[pos[ql]].erase(s[pos[ql]].find(a[i]));
	for(int i=ql;i<=R[pos[ql]];i++)a[i]+=k;
	for(int i=ql;i<=R[pos[ql]];i++)s[pos[ql]].insert(a[i]);
	for(int i=L[pos[qr]];i<=qr;i++)s[pos[qr]].erase(s[pos[qr]].find(a[i]));
	for(int i=L[pos[qr]];i<=qr;i++)a[i]+=k;
	for(int i=L[pos[qr]];i<=qr;i++)s[pos[qr]].insert(a[i]);
	if(pos[ql]+1<=pos[qr]-1)for(int i=pos[ql]+1;i<=pos[qr]-1;i++)tag[i]+=k;
}
inline int query(int ql,int qr,int k)
{
	int res=-1;
	if(pos[ql]==pos[qr])
	{
		for(int i=ql;i<=qr;i++)if(a[i]+tag[pos[ql]]<k)res=max(res,a[i]+tag[pos[ql]]);
		return res;
	}
	for(int i=ql;i<=R[pos[ql]];i++)if(a[i]+tag[pos[ql]]<k)res=max(res,a[i]+tag[pos[ql]]);
	for(int i=L[pos[qr]];i<=qr;i++)if(a[i]+tag[pos[qr]]<k)res=max(res,a[i]+tag[pos[qr]]);
	if(pos[ql]+1<=pos[qr]-1)
		for(int i=pos[ql]+1;i<=pos[qr]-1;i++)
		{
			set<int>::iterator it=s[i].lower_bound(k-tag[i]);
			if(it==s[i].begin())continue;
			it--;
			res=max(res,*it+tag[i]);
		}
	return res;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	t=sqrt(n),cnt=n/t;
	if(n%t)cnt++;
	for(int i=1;i<=cnt;i++)L[i]=(i-1)*t+1,R[i]=min(i*t,n);
	for(int i=1;i<=n;i++)pos[i]=(i-1)/t+1;
	for(int i=1;i<=n;i++)s[pos[i]].insert(a[i]);
	for(int i=1;i<=n;i++)
	{
		int op,l,r,k;scanf("%d%d%d%d",&op,&l,&r,&k);
		if(!op)add(l,r,k);
		else printf("%d
",query(l,r,k));
	}
	return 0;
}

4.区间加+区间和
类似线段树对块打标记。
code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=50010;
const int maxt=250;
int n,t,cnt;
int a[maxn],L[maxt],R[maxt],pos[maxn],sum[maxt],tag[maxt];
inline void add(int ql,int qr,int k)
{
	if(pos[ql]==pos[qr])
	{
		for(int i=ql;i<=qr;i++)a[i]+=k;
		sum[pos[ql]]+=(qr-ql+1)*k;
		return;
	}
	for(int i=ql;i<=R[pos[ql]];i++)a[i]+=k;
	sum[pos[ql]]+=(R[pos[ql]]-ql+1)*k;
	for(int i=L[pos[qr]];i<=qr;i++)a[i]+=k;
	sum[pos[qr]]+=(qr-L[pos[qr]]+1)*k;
	if(pos[ql]+1<=pos[qr]-1)for(int i=pos[ql]+1;i<=pos[qr]-1;i++)tag[i]+=k;
}
inline int query(int ql,int qr,int mod)
{
	int res=0;
	if(pos[ql]==pos[qr])
	{
		for(int i=ql;i<=qr;i++)res=(res+(a[i]+tag[pos[ql]])%mod)%mod;
		return res;
	}
	for(int i=ql;i<=R[pos[ql]];i++)res=(res+(a[i]+tag[pos[ql]])%mod)%mod;
	for(int i=L[pos[qr]];i<=qr;i++)res=(res+(a[i]+tag[pos[qr]])%mod)%mod;
	if(pos[ql]+1<=pos[qr]-1)
		for(int i=pos[ql]+1;i<=pos[qr]-1;i++)res=(res+(sum[i]+(R[i]-L[i]+1)*tag[i]%mod)%mod)%mod;
	return res;
}
signed main()
{
	scanf("%lld",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    t=sqrt(n),cnt=n/t;
    if(n%t)cnt++;
    for(int i=1;i<=cnt;i++)L[i]=(i-1)*t+1,R[i]=min(i*t,n);
    for(int i=1;i<=n;i++)pos[i]=(i-1)/t+1;
    for(int i=1;i<=n;i++)sum[pos[i]]+=a[i];
    for(int i=1;i<=n;i++)
    {
    	int op,l,r,k;scanf("%lld%lld%lld%lld",&op,&l,&r,&k);
    	if(!op)add(l,r,k);
    	else printf("%lld
",query(l,r,k+1));
	}
	return 0;
}

5.区间开根+区间和
记录最大值,发现最大值小于等于1就不修改。
code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=50010;
const int maxt=250;
int n,t,cnt;
int a[maxn],L[maxt],R[maxt],pos[maxn],sum[maxt],maxx[maxt];
inline void change(int ql,int qr)
{
	if(pos[ql]==pos[qr])
	{
		if(maxx[pos[ql]]<=1)return;
		for(int i=ql;i<=qr;i++)sum[pos[ql]]-=a[i],a[i]=sqrt(a[i]),sum[pos[ql]]+=a[i];
		maxx[pos[ql]]=0;
		for(int i=L[pos[ql]];i<=R[pos[ql]];i++)maxx[pos[ql]]=max(maxx[pos[ql]],a[i]);
		return;
	}
	if(maxx[pos[ql]]>1)for(int i=ql;i<=R[pos[ql]];i++)sum[pos[ql]]-=a[i],a[i]=sqrt(a[i]),sum[pos[ql]]+=a[i];
	if(maxx[pos[qr]]>1)for(int i=L[pos[qr]];i<=qr;i++)sum[pos[qr]]-=a[i],a[i]=sqrt(a[i]),sum[pos[qr]]+=a[i];
	if(pos[ql]+1>pos[qr]-1)return;
	for(int i=pos[ql]+1;i<=pos[qr]-1;i++)
	{
		if(maxx[i]<=1)continue;
		sum[i]=maxx[i]=0;
		for(int j=L[i];j<=R[i];j++)a[j]=sqrt(a[j]),sum[i]+=a[j];
		for(int j=L[i];j<=R[i];j++)maxx[i]=max(maxx[i],a[j]);
	}	
}
inline int query(int ql,int qr)
{
    int res=0;
    if(pos[ql]==pos[qr])
    {
        for(int i=ql;i<=qr;i++)res+=a[i];
        return res;
    }
    for(int i=ql;i<=R[pos[ql]];i++)res+=a[i];
    for(int i=L[pos[qr]];i<=qr;i++)res+=a[i];
    if(pos[ql]+1<=pos[qr]-1)
        for(int i=pos[ql]+1;i<=pos[qr]-1;i++)res+=sum[i];
    return res;
}
int main()
{
	scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    t=sqrt(n),cnt=n/t;
    if(n%t)cnt++;
    for(int i=1;i<=cnt;i++)L[i]=(i-1)*t+1,R[i]=min(i*t,n);
    for(int i=1;i<=n;i++)pos[i]=(i-1)/t+1;
	for(int i=1;i<=n;i++)sum[pos[i]]+=a[i],maxx[pos[i]]=max(maxx[pos[i]],a[i]);
    for(int i=1;i<=n;i++)
    {
    	int op,l,r,k;scanf("%d%d%d%d",&op,&l,&r,&k);
    	if(!op)change(l,r);
    	else printf("%d
",query(l,r));
	}
	return 0;
}

6.插入一个数+查第k个数
插入次数达到块长就重构,查询暴力跳。
code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=200010;
const int maxt=500;
int n,t,cnt,tot,num;
int a[maxn],c[maxn];
vector<int>ve[maxt];
inline void rebuild()
{
	num=tot=0;
	for(int i=1;i<=cnt;i++)
	{
		for(unsigned int j=0;j<ve[i].size();j++)a[++tot]=ve[i][j];
		ve[i].clear();
	}
	t=sqrt(tot);cnt=tot/t;
	if(tot%t)cnt++;
	for(int i=1;i<=tot;i++)ve[(i-1)/t+1].push_back(a[i]);
}
inline int find(int x)
{
	int now=1;
	while(x>ve[now].size())x-=ve[now].size(),now++;
	return ve[now][x-1];
}
inline void insert(int x,int k)
{
	num++;
	int now=1;
	while(x>ve[now].size())x-=ve[now].size(),now++;
	ve[now].insert(ve[now].begin()+x-1,k);
	if(num==t)rebuild();
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	t=sqrt(n);cnt=n/t;
	if(n%t)cnt++;
	for(int i=1;i<=n;i++)ve[(i-1)/t+1].push_back(a[i]);
	for(int i=1;i<=n;i++)
	{
		int op,l,r,k;scanf("%d%d%d%d",&op,&l,&r,&k);
		if(!op)insert(l,r);
		else printf("%d
",find(r));
	}
	return 0;
}

7.区间乘+区间加+单点查
像线段树2那样维护标记即可
code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
const int maxt=350;
const int mod=10007;
int n,t,cnt;
int a[maxn],pos[maxn],L[maxt],R[maxt],tag1[maxt],tag2[maxt];
inline void down(int x)
{
	for(int i=L[x];i<=R[x];i++)a[i]=(a[i]*tag2[x]%mod+tag1[x])%mod;
	tag1[x]=0,tag2[x]=1;
}
inline void add(int ql,int qr,int k)
{
	if(pos[ql]==pos[qr])
	{
		down(pos[ql]);
		for(int i=ql;i<=qr;i++)a[i]=(a[i]+k)%mod;
		return;
	}
	down(pos[ql]);
	for(int i=ql;i<=R[pos[ql]];i++)a[i]=(a[i]+k)%mod;
	down(pos[qr]);
	for(int i=L[pos[qr]];i<=qr;i++)a[i]=(a[i]+k)%mod;
	if(pos[ql]+1<=pos[qr]-1)for(int i=pos[ql]+1;i<=pos[qr]-1;i++)tag1[i]=(tag1[i]+k)%mod;
}
inline void mul(int ql,int qr,int k)
{
	if(pos[ql]==pos[qr])
	{
		down(pos[ql]);
		for(int i=ql;i<=qr;i++)a[i]=a[i]*k%mod;
		return;
	}
	down(pos[ql]);
	for(int i=ql;i<=R[pos[ql]];i++)a[i]=a[i]*k%mod;
	down(pos[qr]);
	for(int i=L[pos[qr]];i<=qr;i++)a[i]=a[i]*k%mod;
	if(pos[ql]+1<=pos[qr]-1)
		for(int i=pos[ql]+1;i<=pos[qr]-1;i++)
			tag1[i]=tag1[i]*k%mod,tag2[i]=tag2[i]*k%mod;
}
int main()
{
	scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    t=sqrt(n),cnt=n/t;
    if(n%t)cnt++;
    for(int i=1;i<=cnt;i++)L[i]=(i-1)*t+1,R[i]=min(i*t,n);
    for(int i=1;i<=n;i++)pos[i]=(i-1)/t+1;
    for(int i=1;i<=cnt;i++)tag2[i]=1;
    for(int i=1;i<=n;i++)
    {
    	int op,l,r,k;scanf("%d%d%d%d",&op,&l,&r,&k);
    	if(!op)add(l,r,k);
    	if(op==1)mul(l,r,k);
    	if(op==2)printf("%d
",(a[r]*tag2[pos[r]]%mod+tag1[pos[r]])%mod);
	}
	return 0;
}

8.每次查询一个区间等于某个值的数的个数并将区间赋值为这个值
显然有势能,维护每个块是否被覆盖和覆盖的颜色,每次重构两个散块
code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
const int maxt=350;
int n,t,cnt;
int a[maxn],pos[maxn],L[maxt],R[maxt],col[maxt];
inline void down(int x)
{
	if(col[x]==-1)return;
	for(int i=L[x];i<=R[x];i++)a[i]=col[x];
}
inline void reset(int x,int k)
{
	col[x]=k;
	for(int i=L[x];i<=R[x];i++)if(a[i]!=k)col[x]=-1;
}
inline int query(int ql,int qr,int k)
{
	int res=0;
	if(pos[ql]==pos[qr])
	{
		if(~col[pos[ql]])return (col[pos[ql]]==k)?qr-ql+1:0;
		for(int i=ql;i<=qr;i++)res+=(a[i]==k);
		return res;
	}
	if(~col[pos[ql]])res+=(col[pos[ql]]==k)?R[pos[ql]]-ql+1:0;
	else for(int i=ql;i<=R[pos[ql]];i++)res+=(a[i]==k);
	if(~col[pos[qr]])res+=(col[pos[qr]]==k)?qr-L[pos[qr]]+1:0;
	else for(int i=L[pos[qr]];i<=qr;i++)res+=(a[i]==k);
	if(pos[ql]+1<=pos[qr]-1)
		for(int i=pos[ql]+1;i<=pos[qr]-1;i++)
		{
			if(~col[i]){res+=(col[i]==k)?R[i]-L[i]+1:0;continue;}
			for(int j=L[i];j<=R[i];j++)res+=(a[j]==k);
		}	
	return res;
}
inline void change(int ql,int qr,int k)
{
	if(pos[ql]==pos[qr])
	{
		down(pos[ql]);
		for(int i=ql;i<=qr;i++)a[i]=k;
		reset(pos[ql],k);
		return;
	}
	down(pos[ql]);
	for(int i=ql;i<=R[pos[ql]];i++)a[i]=k;
	reset(pos[ql],k);
	down(pos[qr]);
	for(int i=L[pos[qr]];i<=qr;i++)a[i]=k;
	reset(pos[qr],k);
	if(pos[ql]+1<=pos[qr]-1)for(int i=pos[ql]+1;i<=pos[qr]-1;i++)col[i]=k;
}
int main()
{
	scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    t=sqrt(n),cnt=n/t;
    if(n%t)cnt++;
    for(int i=1;i<=cnt;i++)L[i]=(i-1)*t+1,R[i]=min(i*t,n);
    for(int i=1;i<=n;i++)pos[i]=(i-1)/t+1;
	for(int i=1;i<=cnt;i++)col[i]=-1;
    for(int i=1;i<=n;i++)
    {
    	int l,r,k;scanf("%d%d%d",&l,&r,&k);
    	printf("%d
",query(l,r,k));
    	change(l,r,k);
	}
	return 0;
}

9.区间最小众数
这题相同。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
const int maxt=10100;
const int inf=1e9;
int n,t,cnt,num;
int a[maxn],b[maxn],c[maxn],L[maxt],R[maxt],pos[maxn];
int ans[maxt][maxt];
vector<int>ve[maxn];
inline int read()
{
	char c=getchar();int res=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
	return res*f;
}
inline void pre_work()
{
	sort(b+1,b+n+1);num=unique(b+1,b+n+1)-(b+1);b[0]=inf;
	for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+num+1,a[i])-b;
	for(int i=1;i<=n;i++)ve[a[i]].push_back(i);
	for(int i=1;i<=cnt;i++)
	{
		int maxx=0,res=0;
		for(int j=L[i];j<=n;j++)
		{
			c[a[j]]++;
			if(c[a[j]]>maxx||(c[a[j]]==maxx&&b[a[j]]<b[res]))res=a[j],maxx=c[a[j]];
			if(j%t==0||j==n)ans[i][pos[j]]=res;
		}
		for(int j=L[i];j<=n;j++)c[a[j]]--;
	}
}
inline int calc(int ql,int qr,int k)
{
	return upper_bound(ve[k].begin(),ve[k].end(),qr)-lower_bound(ve[k].begin(),ve[k].end(),ql);
}
inline int query(int ql,int qr)
{
	int maxx=0,res=0;
	if(pos[ql]==pos[qr])
	{
		for(int i=ql;i<=qr;i++)
		{
			int tmp=calc(ql,qr,a[i]);
			if(tmp>maxx||(tmp==maxx&&b[a[i]]<b[res]))maxx=tmp,res=a[i];
		}
		return res;
	}
	for(int i=ql;i<=R[pos[ql]];i++)
	{
		int tmp=calc(ql,qr,a[i]);
		if(tmp>maxx||(tmp==maxx&&b[a[i]]<b[res]))maxx=tmp,res=a[i];
	}
	for(int i=L[pos[qr]];i<=qr;i++)
	{
		int tmp=calc(ql,qr,a[i]);
		if(tmp>maxx||(tmp==maxx&&b[a[i]]<b[res]))maxx=tmp,res=a[i];
	}
	int p1=pos[ql]+1,p2=pos[qr]-1;
	if(p1<=p2)
	{
		int tmp=calc(ql,qr,ans[p1][p2]);
		if(tmp>maxx||(tmp==maxx&&b[ans[p1][p2]]<b[res]))maxx=tmp,res=ans[p1][p2];
	}
	return res;
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=b[i]=read();
	t=30;cnt=n/t;
	if(n%t)cnt++;
	for(int i=1;i<=cnt;i++)L[i]=(i-1)*t+1,R[i]=min(i*t,n);
	for(int i=1;i<=n;i++)pos[i]=(i-1)/t+1;
	pre_work();
	for(int i=1;i<=n;i++)
	{
		int l=read(),r=read();
		printf("%d
",b[query(l,r)]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/nofind/p/11961288.html