CSUSTOJ 1127-区间方差(线段树)

题目链接:http://acm.csust.edu.cn/problem/1127
博客园食用链接:https://www.cnblogs.com/lonely-wind-/p/13509886.html

Description

线段树进阶题

n个数m次操作1 pos x 将位置为pos的值修改为x

2 l r 查询区间l,r的方差乘以区间长度的平方

Input
输入:第一行,n,m

接下来一行n个数接下来m行操作

1   p o s   x   2   l   r ( 1 ≤ n , m ≤ 2 16 ) ( 0 ≤ a i ≤ 1 0 4 ) 1\ pos\ x\ 2\ l\ r(1 \leq n, m \leq 2^{16})(0\leq ai\leq 10^4) 1 pos x 2 l r(1n,m216)(0ai104)

Output
输出每次询问的值

Sample Input 1
4 4
1 2 3 4
2 1 4
1 2 4
2 1 4
2 2 4

Sample Output 1
20
24
2

emmm,题面有点难看。。。可能是当时出题人还不太会Markdown吧。

我们可以先写出它要用的公式: S 2 = ∑ i = l r ( a i − a ˉ ) 2 r − l + 1 × ( r − l + 1 ) 2 S^2=\frac{\sum_{i=l}^r(a_i-\bar{a})^2}{r-l+1}\times (r-l+1)^2 S2=rl+1i=lr(aiaˉ)2×(rl+1)2
接下来我们就是将其化简一波:
S 2 = ∑ i = l r ( a i − a ˉ ) 2 × L S^2=\sum_{i=l}^r(a_i-\bar a)^2\times L S2=i=lr(aiaˉ)2×L
      = L ∑ i = l r a i 2 + ( s u m L ) 2 − 2 a i s u m L \ \ \ \ \ =L\sum_{i=l}^ra_i^2+(\frac{sum}{L})^2-2a_i\frac{sum}{L}      =Li=lrai2+(Lsum)22aiLsum
      = ∑ a i 2 L + ∑ s u m 2 L − ∑ 2 a i s u m \ \ \ \ \ =\sum a_i^2L+\sum \frac{sum^2}{L}-\sum 2a_isum      =ai2L+Lsum22aisum
      = L ∑ a i 2 + L s u m 2 L − 2 s u m ∑ a i \ \ \ \ \ =L\sum a_i^2+L\frac{sum^2}{L}-2sum\sum a_i      =Lai2+LLsum22sumai
      = L ∑ a i 2 + s u m 2 − 2 s u m 2 \ \ \ \ \ =L\sum a_i^2+sum^2-2sum^2      =Lai2+sum22sum2
      = L ∑ a i 2 − s u m 2 \ \ \ \ \ =L\sum a_i^2-sum^2      =Lai2sum2

于是就会发现。。。。这不就是个sb线段树嘛。。。。维护一下区间和,维护一下区间平方和就完事了。

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define lc rt<<1
#define rc rt<<1|1
typedef long long ll;
const int mac=1e5+10;

int a[mac];
ll sum[mac<<2],suma2[mac<<2];
ll ans1=0,ans2=0;

ll pw2(ll x) {return x*x;}

void push_up(int rt)
{
	sum[rt]=sum[lc]+sum[rc];
	suma2[rt]=suma2[lc]+suma2[rc];
}

void build(int l,int r,int rt)
{
	if (l==r){
		sum[rt]=a[l];
		suma2[rt]=pw2(a[l]);
		return;
	}
	int mid=(l+r)>>1;
	build(lson); build(rson);
	push_up(rt);
}

void update(int l,int r,int rt,int pos,int val)
{
	if (l==r){
		sum[rt]=val;
		suma2[rt]=pw2(val);
		return;
	}
	int mid=(l+r)>>1;
	if (mid>=pos) update(lson,pos,val);
	else update(rson,pos,val);
	push_up(rt);
}

void query(int l,int r,int rt,int L,int R)
{
	if (l>=L && r<=R){
		ans1+=suma2[rt];
		ans2+=sum[rt];
		return;
	}
	int mid=(l+r)>>1;
	if (mid>=L) query(lson,L,R);
	if (mid<R) query(rson,L,R);
}

int main(int argc, char const *argv[])
{
	int n,m;
	scanf ("%d%d",&n,&m);
	for (int i=1; i<=n; i++) scanf ("%d",&a[i]);
	build(1,n,1);
	for (int i=1; i<=m; i++){
		int opt,l,r,pos,x;
		scanf ("%d",&opt);
		if (opt==1){
			scanf ("%d%d",&pos,&x);
			update(1,n,1,pos,x);
		}
		else {
			scanf ("%d%d",&l,&r);
			if (l==r) {printf("0\n"); continue;}
			ans1=0; ans2=0;
			query(1,n,1,l,r);
			printf("%lld\n",(r-l+1)*ans1-pw2(ans2));
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值