「SDOI2017」相关分析(用线段树维护平方和)

题目

题目

做法

啊,这道题目一点思路都没有啊。

这么神奇的吗。

就是死命的推式子,这里用一下这位大佬的证明。
在这里插入图片描述在这里插入图片描述在这里插入图片描述
当然,写这个博客主要是想讲一下维护平方和和区间加减。

首先,区间的(lazy)标记具有可加性:((x+k+k)^2=(x+2k)^2),因此,(lazy)标记可以叠加,只要计算每一个(lazy)标记会对维护的值产生多少的贡献即可。

非常的优秀呢。

因为用了别人太多证明了,所以用转载

#include<cstdio>
#include<cstring>
#define  N  110000
#define  NN  210000
using  namespace  std;
typedef  double  LL;
struct  node
{
	int  l,r;//左右儿子
	LL  d,dx2,dxy,dx,dy,lx,ly;//d后面跟的就是维护的数值
	int  tl,tr;//维护区间
	bool  qin;//这个区间是否有被初始化过
}tr[NN];int  len;
LL  a[N],b[N];
inline  void  pushup(int  x)
{
	int  lc=tr[x].l,rc=tr[x].r;
	tr[x].dx=tr[lc].dx+tr[rc].dx;tr[x].dy=tr[lc].dy+tr[rc].dy;
	tr[x].dxy=tr[lc].dxy+tr[rc].dxy;tr[x].dx2=tr[lc].dx2+tr[rc].dx2;
}
inline  void  pushlazy(int  x,LL  lx,LL  ly)
{
	tr[x].dx2+=tr[x].d*lx*lx+2*lx*tr[x].dx;
	tr[x].dxy+=ly*tr[x].dx+lx*tr[x].dy+lx*ly*tr[x].d;
	tr[x].dx+=lx*tr[x].d;tr[x].dy+=ly*tr[x].d;
	tr[x].lx+=lx;tr[x].ly+=ly;
}
inline  void  pushqin(int  x)
{
	int  l=tr[x].tl,r=tr[x].tr;
	tr[x].lx=tr[x].ly=0;
	tr[x].dx=tr[x].dy=(l+r)*tr[x].d/2;
	tr[x].dxy=tr[x].dx2=(LL)r*(r+1)*(2*r+1)/6-(LL)(l-1)*l*(2*l-1)/6;
	tr[x].qin=1;
}
inline  void  pushdown(int  x)
{
	if(tr[x].qin)
	{
		pushqin(tr[x].l);pushqin(tr[x].r);
		tr[x].qin=0;
	}
	if(tr[x].lx  ||  tr[x].ly)
	{
		pushlazy(tr[x].l,tr[x].lx,tr[x].ly);pushlazy(tr[x].r,tr[x].lx,tr[x].ly);
		tr[x].lx=tr[x].ly=0;
	}
}
void  bt(int  l,int  r)
{
	int  now=++len;
	tr[now].d=r-l+1;tr[now].tl=l;tr[now].tr=r;
	if(l==r)tr[now].dx=a[l],tr[now].dx2=a[l]*a[l],tr[now].dxy=a[l]*b[l],tr[now].dy=b[l];
	else
	{
		int  mid=(l+r)/2;
		tr[now].l=len+1;bt(l,mid);
		tr[now].r=len+1;bt(mid+1,r);
		pushup(now);
	}
}
void  change1(int  now,int  l,int  r,int  ll,int  rr,LL  lx,LL  ly)/*非常单纯的修改*/
{
	if(l==ll  &&  r==rr){pushlazy(now,lx,ly);return  ;}
	int  mid=(l+r)/2,lc=tr[now].l,rc=tr[now].r;
	pushdown(now);
	if(rr<=mid)change1(lc,l,mid,ll,rr,lx,ly);
	else  if(mid<ll)change1(rc,mid+1,r,ll,rr,lx,ly);
	else  change1(lc,l,mid,ll,mid,lx,ly),change1(rc,mid+1,r,mid+1,rr,lx,ly);
	pushup(now);
}
void  change2(int  now,int  l,int  r,int  ll,int  rr)
{
	if(l==ll  &&  r==rr)
	{
		pushqin(now);
		return  ;
	}
	int  mid=(l+r)/2,lc=tr[now].l,rc=tr[now].r;
	pushdown(now);
	if(rr<=mid)change2(lc,l,mid,ll,rr);
	else  if(mid<ll)change2(rc,mid+1,r,ll,rr);
	else  change2(lc,l,mid,ll,mid),change2(rc,mid+1,r,mid+1,rr);
	pushup(now);
}
double  ans_x,ans_y,ans_xy,ans_x2;
void  findans(int  now,int  l,int  r,int  ll,int  rr)
{
	if(l==ll  &&  r==rr)
	{
		ans_x+=tr[now].dx;ans_y+=tr[now].dy;
		ans_xy+=tr[now].dxy;ans_x2+=tr[now].dx2;
		return  ;
	}
	int  mid=(l+r)/2,lc=tr[now].l,rc=tr[now].r;
	pushdown(now);
	if(rr<=mid)findans(lc,l,mid,ll,rr);
	else  if(mid<ll)findans(rc,mid+1,r,ll,rr);
	else  findans(lc,l,mid,ll,mid),findans(rc,mid+1,r,mid+1,rr);
}
int  n,m;
int  main()
{
	scanf("%d%d",&n,&m);
	for(int  i=1;i<=n;i++)scanf("%lf",&a[i]);
	for(int  i=1;i<=n;i++)scanf("%lf",&b[i]);
	bt(1,n);
	for(int  i=1;i<=m;i++)
	{
		int  type;scanf("%d",&type);
		if(type==1)
		{
			int  l,r;scanf("%d%d",&l,&r);
			ans_x=ans_y=ans_xy=ans_x2=0;
			findans(1,1,n,l,r);
			printf("%lf
",(ans_xy-ans_x*ans_y/(r-l+1))/(ans_x2-ans_x*ans_x/(r-l+1)));
		}
		else  if(type==2)
		{
			int  l,r;LL  x,y;scanf("%d%d%lf%lf",&l,&r,&x,&y);
			change1(1,1,n,l,r,x,y);
		}
		else  if(type==3)
		{
			int  l,r;LL  x,y;scanf("%d%d%lf%lf",&l,&r,&x,&y);
			change2(1,1,n,l,r);
			change1(1,1,n,l,r,x,y);
		}
	}
	return  0;
}

小结

在遇到平均数的题目时们可以尝试把平均数拆开化化式子来做。

【推广】 免费学中医,健康全家人
原文地址:https://www.cnblogs.com/zhangjianjunab/p/13820150.html