分块练习小结

发布时间 2023-08-28 16:33:13作者: g1ove

P2801

题意:一个序列,两种操作

  • 1 区间加上一个数
  • 2 给定 \(x\) 区间查询有多少个数大于 \(x\)

暴力分块搞
很难搞多少个数大于
考虑维护每个小块的排序好的数组 每次修改小块完排序 大块打标记
查询时二分一下即可
时间复杂度 \(O(q\sqrt n \log n)\)

#include<bits/stdc++.h>
#define N 1000005
#define ll long long
using namespace std;
int n,m;
ll mod;
int block,tot,L[1005],R[1005],bel[N];
int tag[1005],a[N],d[N];
void reset(int x)
{
	for(int i=L[x];i<=R[x];i++)
		d[i]=a[i];
	sort(d+L[x],d+1+R[x]);
}
void build()
{
	block=sqrt(n);
	tot=n/block;
	if(n%block) tot++;
	for(int i=1;i<=tot;i++)
		L[i]=R[i-1]+1,R[i]=i*block,reset(i);
	R[tot]=n;
	for(int i=1;i<=n;i++)
		bel[i]=(i-1)/block+1;
}
void updata(int l,int r,int k)
{
	int ls=bel[l],rs=bel[r];
	if(ls==rs)
	{
		for(int i=l;i<=r;i++)
			a[i]+=k;
		reset(ls);
		return;
	}
	for(int i=l;i<=R[ls];i++)
		a[i]+=k;
	reset(ls);
	for(int i=L[rs];i<=r;i++)
		a[i]+=k;
	reset(rs);
	for(int i=ls+1;i<=rs-1;i++)
		tag[i]+=k;
}
int query(int l,int r,int k)
{
	int ls=bel[l],rs=bel[r],ans=0;
	if(ls==rs)
	{
		for(int i=l;i<=r;i++)
			if(a[i]+tag[ls]>=k) ans++;
		return ans;
	}
	for(int i=l;i<=R[ls];i++)
		if(tag[ls]+a[i]>=k) ans++;
	for(int i=L[rs];i<=r;i++)
		if(tag[rs]+a[i]>=k) ans++;
	for(int i=ls+1;i<=rs-1;i++)
		ans+=R[i]-(lower_bound(d+L[i],d+1+R[i],k-tag[i])-d)+1; 
	return ans;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	build();
	while(m--)
	{
		char c;
		int l,r,x;
		cin>>c>>l>>r>>x;
		if(c=='M') 
			updata(l,r,x);
		else
			cout<<query(l,r,x)<<"\n";
	}
	return 0;
}

P4145

题意:给定序列,两种操作

  • 查询一段区间和
  • 对一段区间每个数 \(x\to \sqrt x\)

根据每个数最多只会开根 \(12\) 次打标记看看哪些区间不用修改跳过即可

#include<bits/stdc++.h>
#define N 100005
#define ll long long
using namespace std;
int n,m;
int block,tot,L[N],R[N],bel[N];
ll a[N],sum[N],tag[N];
void build()
{
	block=sqrt(n);
	tot=n/block;
	if(n%block) tot++;
	for(int i=1;i<=n;i++)
		bel[i]=(i-1)/block+1,sum[bel[i]]+=a[i];
	for(int i=1;i<=tot;i++)
		L[i]=R[i-1]+1,R[i]=i*block;
	R[tot]=n;
}
void modify(int l,int r)
{
	int ls=bel[l],rs=bel[r];
	if(ls==rs)
	{
		for(int i=l;i<=r;i++)
			sum[ls]-=a[i],a[i]=sqrt(a[i]),sum[ls]+=a[i];
		return;
	}
	for(int i=l;i<=R[ls];i++)
	{
		sum[ls]-=a[i];
		a[i]=sqrt(a[i]);
		sum[ls]+=a[i];
	}
	for(int i=L[rs];i<=r;i++)
	{
		sum[rs]-=a[i];
		a[i]=sqrt(a[i]);
		sum[rs]+=a[i];
	}
	for(int i=ls+1;i<=rs-1;i++)
		if(sum[i]>R[i]-L[i]+1)
		{
			sum[i]=0;
			for(int j=L[i];j<=R[i];j++)
				a[j]=sqrt(a[j]),sum[i]+=a[j];
		}
}
ll query(int l,int r)
{
	ll ans=0;
	int ls=bel[l],rs=bel[r];
	if(ls==rs)
	{
		for(int i=l;i<=r;i++)
			ans+=a[i];
		return ans;
	}
	for(int i=l;i<=R[ls];i++)
		ans+=a[i];
	for(int i=L[rs];i<=r;i++)
		ans+=a[i];
	for(int i=ls+1;i<=rs-1;i++)
		ans+=sum[i];
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	build();
	scanf("%d",&m);
	while(m--)
	{
		int opr,l,r;
		scanf("%d%d%d",&opr,&l,&r);
		if(l>r) swap(l,r);
		if(opr) printf("%lld\n",query(l,r));
		else modify(l,r);
	}
	return 0;
}

P4168

十分重要的一道题
给定一个序列 每次问一段区间的众数
这道题有一个思想:预处理
因为是静态区间 可以用 \(O(n\sqrt n)\) 的时间预处理好每一块到另一块的众数
这样只需要枚举一下小块的数和一个大众数即可
时间复杂度 \(O(n\sqrt n)\)

#include<bits/stdc++.h>
#define N 40005
#define ll long long
using namespace std;
int n,m,sum[N];
int block,tot,L[1205],R[1205],bel[N];
int a[N],b[N];
int s[1205][N],f[1205][1205];
// s 前缀和 f i,j i->j块众数 
void build()
{
	block=pow(n,(1.0/3));
	tot=n/block;
	if(n%block) tot++;
	for(int i=1;i<=tot;i++)
		L[i]=R[i-1]+1,R[i]=i*block;
	R[tot]=n;
	for(int i=1;i<=n;i++)
		bel[i]=(i-1)/block+1;
	for(int i=1;i<=n;i++)
		for(int j=bel[i];j<=tot;j++)
			s[j][a[i]]++;
	for(int i=1;i<=tot;i++)
	{
		for(int j=i;j<=tot;j++)
		{
			f[i][j]=f[i][j-1];
			for(int k=L[j];k<=R[j];k++)
			{
				sum[a[k]]++;
				if(sum[a[k]]>sum[f[i][j]]||sum[a[k]]==sum[f[i][j]]&&a[k]<f[i][j]) f[i][j]=a[k];
			}
		}
		for(int j=L[i];j<=n;j++)	
			sum[a[j]]=0;
	}
}
int query(int l,int r)
{
	int ls=bel[l],rs=bel[r],ans=0;
	if(rs-ls<=2)
	{
		for(int i=l;i<=r;i++)
		{
			sum[a[i]]++;
			if(sum[a[i]]>sum[ans]||sum[a[i]]==sum[ans]&&ans>a[i]) ans=a[i];
		}
		for(int i=l;i<=r;i++)
			sum[a[i]]=0;
		return ans;
	}
	for(int i=l;i<=R[ls];i++) sum[a[i]]=s[rs-1][a[i]]-s[ls][a[i]];
	for(int i=L[rs];i<=r;i++) sum[a[i]]=s[rs-1][a[i]]-s[ls][a[i]];
	for(int i=l;i<=R[ls];i++)
	{
		sum[a[i]]++;
		if(sum[a[i]]>sum[ans]||sum[a[i]]==sum[ans]&&ans>a[i]) ans=a[i];
	}
	for(int i=L[rs];i<=r;i++)
	{
		sum[a[i]]++;
		if(sum[a[i]]>sum[ans]||sum[a[i]]==sum[ans]&&ans>a[i]) ans=a[i];
	}
	int t=f[ls+1][rs-1],v=s[rs-1][t]-s[ls][t];
	for(int i=l;i<=R[ls];i++) v+=(a[i]==t);
	for(int i=L[rs];i<=r;i++) v+=(a[i]==t);
	if(v>sum[ans]||v==sum[ans]&&ans>t) ans=t;
	for(int i=l;i<=R[ls];i++) sum[a[i]]=0;
	for(int i=L[rs];i<=r;i++) sum[a[i]]=0;
	return ans;
}
int main()
{
//	freopen("P4168_1.in","r",stdin);
//	freopen("my.txt","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]),b[i]=a[i];
	sort(b+1,b+1+n);
	int len=unique(b+1,b+1+n)-b;
	for(int i=1;i<=n;i++)
		a[i]=lower_bound(b+1,b+len,a[i])-b;
	build();
	int last=0;
	while(m--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		l=(l+last-1)%n+1;
		r=(r+last-1)%n+1;
		if(l>r) swap(l,r);
		last=b[query(l,r)];
		printf("%d\n",last);
	}
	return 0;
}