zoukankan      html  css  js  c++  java
  • 【JZOJ4231】寻找神格【分块】

    题目大意:

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

    1. 0 x y0 x y,表示将位置xx的数字增加yy
    2. 1 x y z1 x y z,表示[x,y)[x,y)中的数字全部加zz
    3. 2 x y2 x y,表示询问xyxsim y之间的数字和。
    4. 3 x y3 x y,表示询问xyxsim y之间的数字方差。

    思路:

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

    整理得
    x12+x22+...+xn2+2ave(x1+x2+...+xn)+n×ave2x1^2+x2^2+...+xn^2+2ave(x1+x2+...+xn)+n imes ave^2
    sum=x1+x2+...+xnsum=x1+x2+...+xn带入得
    x12+x22+...+xn2+2×ave×sum+n×ave2x1^2+x2^2+...+xn^2+2 imes ave imes sum+n imes ave^2
    那么就再维护每个块的平方和就可以在O(n)O(sqrt{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
    ",ask1(x,y));
    		}
    		else if (x==3)
    		{
    			x=read(),y=read();
    			printf("%0.10Lf
    ",ask2(x,y));
    		}
    		
    	}
    	return 0;
    }
    
  • 相关阅读:
    FileUpload1上传控件
    docker如何push镜像到docker hub个人的仓库
    docker的ubuntu镜像无ifconfig和ping命令
    keystone同步数据库的时候提示error
    openstack安装dashboard后访问horizon出错 500 or 504
    装了ubuntu之后,只能进入ubuntu系统,不能进入windows系统
    Kernal Panic
    无法获得锁 /var/lib/dpkg/lock -open
    用户 'NT AUTHORITYIUSR' 登录失败
    配置错误:不能在此路径中使用此配置节。
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998383.html
Copyright © 2011-2022 走看看