线段树 (维护 区间平方和 Lazy标记)

链接:https://ac.nowcoder.com/acm/contest/917/J
来源:牛客网
 

外挂

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 65536K,其他语言131072K
64bit IO Format: %lld

题目描述

我的就是我的,你也是我的,记住了,狐狸!

                                                                      ——韩信-白龙吟

 

对于打赌输了的小T会遭受到制裁,小s修改了数据库使他可以派出许多军队来围攻小T.

 

很不幸,小T与小s打赌打输了,现在小T遭受着枪林弹雨与十面埋伏,因为小T是神所以他决定要扭转局势。

 

他要修改数据库!

 

数据总库的信号墙有n个电极插头,每个插头有一个信号aia_iai​,

 

小T可以使在区间[ l,r ][\ l,r\ ][ l,r ]内的所有信号加上一个值k。

 

对于区间[ l,r ][\ l,r\ ][ l,r ]的信号强度有一个计算公式:

 

我们定义

f(k)=ak×∑j=k+1rajf(k)=a_k \times \sum_{j=k+1}^r a_jf(k)=ak​×∑j=k+1r​aj​

 

则信号强度就为:

∑i=lrf(i)\sum_{i=l}^r f(i)∑i=lr​f(i)

 

你可以认为f(i)就是第i个插头的信号强度。

 

现在小T一会儿修改信号值,一会儿询问信号强度,你是数据库的管理员,为了不被小TD,所以你要告诉他信号强度是多少。

 

 

注:本系列题不按难度排序哦

输入描述:

第一行两个整数n,Q

第二行n个整数代表a

后Q行代表操作:

一操作:1 l r x1\ l\ r\ x1 l r x代表区间[ l,r ][\ l,r\ ][ l,r ]加x。

二操作:2 l r2\ l\ r2 l r代表区间询问。

输出描述:

每一行一个数字,表示对于一个二操作的答案。

示例1

输入

复制

5 2
1 2 3 4 5
1 1 2 1
2 1 2

输出

复制

6

说明

 

样例解释:1 1 2 1使a[1]~a[2]的值每个都加了1, 即a[1]=2, a[2]=3,所以2 1 2=a[1]*a[2]=2*3=6

 

保证所有二操作的答案都是在long longlong\ longlong long范围内(如果你不相信,可以写高精)。


时空限制为标程的5倍,放心卡常。

备注:

 

100%  1≤n,Q≤105100 \% \ \ 1 \le n,Q \le 10^5100%  1≤n,Q≤105

对于所有ai≤100a_i \le 100ai​≤100

【此图转自https://blog.csdn.net/qq_41997978/article/details/92384967

#include<bits/stdc++.h> 
#define ll long long 
#define ls o*2
#define rs o*2+1
#define mid (l+r)/2
using namespace std;
ll tree[4*100005],tag[4*100005],qtree[4*100005];
//tree[]区间和  qtree[]区间平方和 
ll a[100005];
int n,Q;
void pushdown(int o,int l,int r)
{
	if(!tag[o]||l==r) return ;//下传 对子节点修改 而不是对当前节点!!! 
	qtree[ls]+= 2*tag[o]*tree[ls] + tag[o]*tag[o]*(mid-l+1);
	qtree[rs]+= 2*tag[o]*tree[rs] + tag[o]*tag[o]*(r-mid);
	//先修改区间平方和  利用之前区间和 
	
	tree[ls]+=tag[o]*(mid-l+1);
	tree[rs]+=tag[o]*(r-mid);
	
	
	tag[ls]+=tag[o];
	tag[rs]+=tag[o];
	tag[o]=0;
}
void up(int o,int l,int r,int ql,int qr,ll v)
{
	if(ql<=l&&r<=qr)
	{
		qtree[o]+= 2*tree[o]*v + v*v*(r-l+1);//每个数都加 v*v+2*v*a[i] 
		tree[o]+= v*(r-l+1);
		tag[o]+=v;
		return ; 
	}
	pushdown(o,l,r);//下传 
	if(ql<=mid) up(ls,l,mid,ql,qr,v);
	if(mid<qr) up(rs,mid+1,r,ql,qr,v);
	
	tree[o]=tree[ls]+tree[rs];//pushup 维护区间和 区间平方和 
	qtree[o]=qtree[ls]+qtree[rs];
	return ;
}
ll qu_sum(int o,int l,int r,int ql,int qr)//获取区间和 
{
	if(ql<=l&&r<=qr)
	{
		return tree[o];
	}
	pushdown(o,l,r);
	ll ans=0;
	if(ql<=mid) ans+=qu_sum(ls,l,mid,ql,qr);
	if(mid<qr) ans+=qu_sum(rs,mid+1,r,ql,qr);
	return ans;
	
}
ll qu_qtree(int o,int l,int r,int ql,int qr)//获取区间平方和 
{
	if(ql<=l&&r<=qr)
	{
		return qtree[o];
	}
	pushdown(o,l,r);
	ll ans=0;
	if(ql<=mid) ans+=qu_qtree(ls,l,mid,ql,qr);
	if(mid<qr) ans+=qu_qtree(rs,mid+1,r,ql,qr);
	return ans;
	
}
int main() 
{
	scanf("%d%d",&n,&Q);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		up(1,1,n,i,i,a[i]);
	}
	while(Q--)
	{
		int t;
		scanf("%d",&t);
		if(t==1)
		{
			int l,r;
			ll x;
			scanf("%d%d%lld",&l,&r,&x);
			up(1,1,n,l,r,x);
			
		}
		else 
		{
			int l,r;
			scanf("%d%d",&l,&r);
			ll qtreenum=qu_qtree(1,1,n,l,r);
			ll sum=qu_sum(1,1,n,l,r);
			printf("%lld\n",(sum*sum-qtreenum)/2);
		}
	}
	
}
//6 6
//1 1 1 1 1 1
//
//1 1 1 1 1 1
//1 2 4 6

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值