牛客月赛15-外挂-(线段树+数论性质)

J

题意:
给你一个数组,然后有两种操作,一种操作是让l到r都加上x,另一种是查询l,r的值,这个值的定义是
思考:
其实看起来这个式子很复杂,实际上看看就是对于l到r中的每个数,都乘以他后面的数,这不就是l到r区间内的数两两相乘吗。但是这怎么去维护呢,有个公式就是,区间内任意两个数相乘=(区间和的平方-区间平方的和)/2。所以就用线段树去维护区间和,然后区间平方的和怎么弄呢,其实就是比如aa+bb,如果都加x,那么就是(a+x)(a+x)+(b+x)(b+x)=aa+bb+xx2+2x(a+b),很明显了就是ans+区间长度xx+区间和2x。

代码:

const int N = 2e5+5,M = 2000;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
const double pai = acos(-1);

struct Node{
	int L,R;
	int laz;
	int sum;
	int ans;
}t[4*N];

int T,n,m,k;
int va[N];

void pushup(int node)
{
	t[node].sum = t[node_l].sum+t[node_r].sum;
	t[node].ans = t[node_l].ans+t[node_r].ans;
}

void pushdown(int node)
{
	int laz = t[node].laz;
	if(laz)
	{
		t[node_l].laz += laz;
		t[node_l].ans += (t[node_l].R-t[node_l].L+1)*laz*laz+2*t[node_l].sum*laz;
		t[node_l].sum += (t[node_l].R-t[node_l].L+1)*laz;
		t[node_r].laz += laz;
		t[node_r].ans += (t[node_r].R-t[node_r].L+1)*laz*laz+2*t[node_r].sum*laz;
		t[node_r].sum += (t[node_r].R-t[node_r].L+1)*laz;
		t[node].laz = 0;
	}	
}

void build(int node,int l,int r)
{
	t[node].L = l,t[node].R = r;
	if(l==r)
	{
		t[node].sum = va[l];
		t[node].ans = va[l]*va[l];
		return ;
	}
	int mid = l+r>>1;
	build(node_l,l,mid);build(node_r,mid+1,r);
	pushup(node);	
}

void update(int node,int l,int r,int value)
{
	if(t[node].L>=l&&t[node].R<=r)
	{
		t[node].ans += (t[node].R-t[node].L+1)*value*value+2*t[node].sum*value;
		t[node].sum += (t[node].R-t[node].L+1)*value;
		t[node].laz += value;
		return ;
	}
	pushdown(node);
	int mid = (t[node].L+t[node].R)>>1;
	if(l<=mid) update(node_l,l,r,value);
	if(r>mid) update(node_r,l,r,value);
	pushup(node);	
}

int query1(int node,int l,int r)
{
	if(t[node].L>=l&&t[node].R<=r) return t[node].sum;
	pushdown(node);
	int mid = (t[node].L+t[node].R)>>1;
	if(r<=mid) return query1(node_l,l,r);
	else if(l>mid) return query1(node_r,l,r);
	else return query1(node_l,l,mid)+query1(node_r,mid+1,r);
}

int query2(int node,int l,int r)
{
	if(t[node].L>=l&&t[node].R<=r) return t[node].ans;
	pushdown(node);
	int mid = (t[node].L+t[node].R)>>1;
	if(r<=mid) return query2(node_l,l,r);
	else if(l>mid) return query2(node_r,l,r);
	else return query2(node_l,l,mid)+query2(node_r,mid+1,r);
}

signed main()
{
	IOS;
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>va[i];
	build(1,1,n);
	while(m--)
	{
		int op,a,b,c;
		cin>>op;
		if(op==1)
		{
			cin>>a>>b>>c;
			update(1,a,b,c);
		}
		else
		{
			cin>>a>>b;
			int t1 = query1(1,a,b),t2 = query2(1,a,b);
			int anw = (t1*t1-t2)/2;
			cout<<anw<<"\n";
		}
	}
	return 0;
}

总结:
多多尝试,多多推推式子不要畏惧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值