【JZOJ4231】寻找神格【分块】

本文介绍了一种使用分块算法处理大规模数列的高效解决方案,支持四种操作:局部增减、区间加法、区间求和及区间方差计算。通过预处理和维护每个数据块的和与平方和,实现在O(√n)时间内快速响应各种查询和更新,特别适合于n≤100000的数据规模。

题目大意:

一个长度为nnn的数列,要求支持444个操作:

  1. 0 x y0\ x\ y0 x y,表示将位置xxx的数字增加yyy
  2. 1 x y z1\ x\ y\ z1 x y z,表示[x,y)[x,y)[x,y)中的数字全部加zzz
  3. 2 x y2\ x\ y2 x y,表示询问x∼yx\sim yxy之间的数字和。
  4. 3 x y3\ x\ y3 x y,表示询问x∼yx\sim yxy之间的数字方差。

思路:

n≤100000n\leq 100000n100000,考虑分块。(个人感觉分块比线段树好打)。
对于222操作,很明显是需要在每个块内维护块内和。所以用sum[i]sum[i]sum[i]表示块iii中的数字和。
对于333操作,考虑将方差用完全平方公式拆开。
1n[(x1−ave)2+(x2−ave)2+...+(xn−ave)2]\frac{1}{n}[(x1-ave)^2+(x2-ave)^2+...+(xn-ave)^2]n1[(x1ave)2+(x2ave)2+...+(xnave)2]
先不考虑1n\frac{1}{n}n1
(x1−ave)2+(x2−ave)2+...+(xn−ave)2(x1-ave)^2+(x2-ave)^2+...+(xn-ave)^2(x1ave)2+(x2ave)2+...+(xnave)2
拆开得
(x12−2×x1×ave)+(x22+2×x2×ave)+...+(xn2+2×xn×ave)(x1^2-2\times x1\times ave)+(x2^2+2\times x2\times ave)+...+(xn^2+2\times xn\times ave)(x122×x1×ave)+(x22+2×x2×ave)+...+(xn2+2×xn×ave)
去括号+整理得
x12+x22+...+xn2+2ave×x1+2ave×x2+...2ave×xn+ave2+ave2+...+ave2x1^2+x2^2+...+xn^2+2ave\times x1+2ave\times x2+...2ave\times xn+ave^2+ave^2+...+ave^2x12+x22+...+xn2+2ave×x1+2ave×x2+...2ave×xn+ave2+ave2+...+ave2

整理得
x12+x22+...+xn2+2ave(x1+x2+...+xn)+n×ave2x1^2+x2^2+...+xn^2+2ave(x1+x2+...+xn)+n\times ave^2x12+x22+...+xn2+2ave(x1+x2+...+xn)+n×ave2
sum=x1+x2+...+xnsum=x1+x2+...+xnsum=x1+x2+...+xn带入得
x12+x22+...+xn2+2×ave×sum+n×ave2x1^2+x2^2+...+xn^2+2\times ave\times sum+n\times ave^2x12+x22+...+xn2+2×ave×sum+n×ave2
那么就再维护每个块的平方和就可以在O(n)O(\sqrt{n})O(n)内求出答案了。
维护倒是不难,熟用完全平方公式+++注意细节即可。


代码:

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef long double ld;

const int N=100010;
const int M=350;
int n,Q,T,L[M],R[M],pos[N];
ll ans[M],add[M],sum[M],a[N],Read,f,x,y,z;
char ch;

ll read()
{
	Read=0;
	f=1;
	ch=getchar();
	while (ch<'0'||ch>'9')
	{
		if (ch=='-') f=-f;
		ch=getchar();
	}
	while (ch>='0'&&ch<='9')
		Read=(Read<<3)+(Read<<1)+ch-48,ch=getchar();
	return Read*f;
}

void change(int l,int r,ll z)  //修改
{
	int q=pos[l],p=pos[r];
	if (q==p)  //一个块暴力修改
	{
		for (register int i=l;i<=r;i++)
			ans[q]+=2*(a[i]+add[q])*z+z*z,a[i]+=z,sum[q]+=z;
		return;
	}
	for (register int i=l;i<=R[q];i++)
		ans[q]+=2*(a[i]+add[q])*z+z*z,a[i]+=z,sum[q]+=z;
	for (register int i=L[p];i<=r;i++)  //分开
		ans[p]+=2*(a[i]+add[p])*z+z*z,a[i]+=z,sum[p]+=z;
	for (register int i=q+1;i<p;i++)  //lazy修改
		add[i]+=z,ans[i]+=2*sum[i]*z+z*z*(R[i]-L[i]+1),sum[i]+=z*(R[i]-L[i]+1);
}

ll ask1(int l,int r)  //询问和
{
	int q=pos[l],p=pos[r];
	ll s=0;
	if (q==p)
	{
		for (register int i=l;i<=r;i++) s+=(ll)(a[i]+add[q]);  //暴力求和
		return s;
	}
	for (register int i=l;i<=R[q];i++) s+=(ll)(a[i]+add[q]);
	for (register int i=L[p];i<=r;i++) s+=(ll)(a[i]+add[p]);
	for (register int i=q+1;i<p;i++) s+=sum[i];
	return s;
}

ld ask2(int l,int r)  //询问方差
{
	int q=pos[l],p=pos[r];
	ll Ans=0,Sum=0;
	if (q==p)
	{
		for (register int i=l;i<=r;i++)  //暴力
			Sum+=a[i]+add[q];
		ld Ave=(ld)Sum/(ld)(r-l+1),s=0.0;
		for (register int i=l;i<=r;i++)
			s+=((ld)a[i]+(ld)add[q]-Ave)*((ld)a[i]+(ld)add[q]-Ave);
		return (ld)s/(ld)(r-l+1);
	}
	for (register int i=l;i<=R[q];i++)  //左边
	{
		Ans+=(a[i]+add[q])*(a[i]+add[q]);
		Sum+=a[i]+add[q];
	}
	for (register int i=L[p];i<=r;i++)  //右边
	{
		Ans+=(a[i]+add[p])*(a[i]+add[p]);
		Sum+=a[i]+add[p];
	}
	for (register int i=q+1;i<p;i++)  //中间直接利用lazy
	{
		Ans+=ans[i];
		Sum+=sum[i];
	}
	ld Ave=(ld)Sum/(ld)(r-l+1);
	return ((ld)Ans-2.0*(ld)Sum*Ave+(ld)(r-l+1)*Ave*Ave)/(ld)(r-l+1);
	//完全平方公式输出
}

int main()
{ 
	n=read(),Q=read();
	for (register int i=1;i<=n;i++)
		a[i]=read();
	T=sqrt(n);
	if (T*T<n) T++;
	for (register int i=1;i<=T;i++)
	{
		L[i]=R[i-1]+1;
		R[i]=min(i*T,n);
		for (register int j=L[i];j<=R[i];j++)
		{
			pos[j]=i;
			ans[i]+=a[j]*a[j];
			sum[i]+=a[j];
		}
	}
	while (Q--)
	{
		x=read();
		if (x==0)
		{
			x=read(),y=read();
			change(x,x,y);
		}
		else if (x==1)
		{
			x=read(),y=read(),z=read();
			change(x,y,z);
		}
		else if (x==2)
		{
			x=read(),y=read();
			printf("%lld\n",ask1(x,y));
		}
		else if (x==3)
		{
			x=read(),y=read();
			printf("%0.10Lf\n",ask2(x,y));
		}
		
	}
	return 0;
}
MATLAB主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性内容概要:本文主要介绍了一种在MATLAB环境下实现的主动噪声和振动控制算法,该算法针对较大的次级路径变化具有较强的鲁棒性。文中详细阐述了算法的设计原理与实现方法,重点解决了传统控制系统中因次级路径动态变化导致性能下降的问题。通过引入自适应机制和鲁棒控制策略,提升了系统在复杂环境下的稳定性和控制精度,适用于需要高精度噪声与振动抑制的实际工程场景。此外,文档还列举了多个MATLAB仿真实例及相关科研技术服务内容,涵盖信号处理、智能优化、机器学习等多个交叉领域。; 适合人群:具备一定MATLAB编程基础和控制系统理论知识的科研人员及工程技术人员,尤其适合从事噪声与振动控制、信号处理、自动化等相关领域的研究生和工程师。; 使用场景及目标:①应用于汽车、航空航天、精密仪器等对噪声和振动敏感的工业领域;②用于提升现有主动控制系统对参数变化的适应能力;③为相关科研项目提供算法验证与仿真平台支持; 阅读建议:建议读者结合提供的MATLAB代码进行仿真实验,深入理解算法在不同次级路径条件下的响应特性,并可通过调整控制参数进一步探究其鲁棒性边界。同时可参考文档中列出的相关技术案例拓展应用场景。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值