Ryuji doesn't want to study(2018徐州网络赛H题,线段树)

徐州网络赛 - H

Ryuji doesn’t want to study

Ryuji is not a good student, and he doesn’t want to study. But there are n books he should learn, each book has its knowledge a[i].

Unfortunately, the longer he learns, the fewer he gets.

That means, if he reads books from ll to rr, he will get a[l]×L+a[l+1]×(L−1)+⋯+a[r−1]×2+a[r] (L is the length of [l,r] that equals to r-l+1).

Now Ryuji has qq questions, you should answer him:

  1. If the question type is 1, you should answer how much knowledge he will get after he reads books [l,r].

  2. If the question type is 2, Ryuji will change the ith book’s knowledge to a new value.

Input

First line contains two integers nn and qq (n,q≤100000).

The next line contains n integers represent ai(ai≤1e9) .

Then in next q line each line contains three integers a, b, c, if a=1, it means question type is 1, and b, c represents [l,r]. if a=2 , it means question type is 2 , and b, c means Ryuji changes the bth book’ knowledge to c

Output

For each question, output one line with one integer represent the answer.

样例输入

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

样例输出

10
8

分析:

题意就很像是求线段树的题目,有两个操作,操作1是单点修改,操作2是求一段区间的某种和,这个和是al * len(区间长度) + a(l+1) * (len-1)… + ar * 1,下文就简称为乘积和。其实对于这个操作我们可以在计算时这样考虑,首先用线段树保存两个东西,一个是区间和,另一个就是区间与区间长度乘积和(就是题目要求的那个和),我们用两个小区间来求大区间的时候就可以用左区间的乘积和 + 右区间的乘积和 + 左区间的区间和 * 右区间的长度。最后,因为查询的时候需要乘积和和区间和,我就存了一个结构体来同时返回两个东西。

举个例子:

1 2 3 4 5
假设左区间为[1,3],区间和就是1 + 2 + 3=6,区间乘积和就是1 * 3 + 2 * 2 + 3 * 1 = 10
右区间为[4,5],区间和就是4+5=9,区间乘积和就是4 * 2 + 5 * 1 = 13
整个区间的乘积和就是:
1 * 5 + 2 * 4 + 3 * 3 + 4 * 2 + 5 * 1 = 35
10 + 13 + 6 * 2 = 35
算出来的答案是一样的。
实际上我们直接相加乘积和就是
1 * 3 + 2 * 2 + 3 * 1 + 4 * 2 + 5 * 1
后半部分是完全一样的,前半部分少的就是右半部分的长度个前半部分区间和。
这个其实稍微想一下就可以想出来。

参考代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAXN 100005
#define left rt<<1
#define right rt<<1|1
typedef long long ll;
using namespace std;

int n,q,op,l,r,p;
ll c;

struct SegTree {
	int l,r;
	// sum为区间和,sum1为乘积和
	ll sum,sum1,len;
}t[MAXN<<2];

struct node {
	ll sum,sum1;
	node(){sum=0; sum1=0;}
}ans;

void pushup(int rt) {
	t[rt].sum=t[left].sum+t[right].sum;
	// 这一部分就是乘积和的pushup
	t[rt].sum1=t[left].sum1+t[right].len*t[left].sum+t[right].sum1;
}

// 建树和更新都是正常的
void build(int l,int r,int rt) {
	t[rt].l=l; t[rt].r=r; t[rt].len=r-l+1;
	if (l==r) {
		scanf("%lld",&t[rt].sum);
		t[rt].sum1=t[rt].sum;
		return;
	}
	int m=(l+r)>>1;
	build(l,m,left);
	build(m+1,r,right);
	pushup(rt);
}

void update(int p,ll c,int rt) {
	if (t[rt].l==t[rt].r) {
		t[rt].sum=t[rt].sum1=c;
		return;
	}
	int m=(t[rt].l+t[rt].r)>>1;
	if (p<=m) update(p,c,left);
	if (p>m) update(p,c,right);
	pushup(rt);
}

// 返回的是结构体
node query(int L,int R,int rt) {
	node tmp,tmpl,tmpr;
	if (L<=t[rt].l && t[rt].r<=R) {
		tmp.sum=t[rt].sum; tmp.sum1=t[rt].sum1;
		return tmp;
	}
	int m=(t[rt].l+t[rt].r)>>1;
	if (L<=m) tmpl=query(L,R,left);
	if (R>m) tmpr=query(L,R,right);
	tmp.sum=tmpl.sum+tmpr.sum;
	tmp.sum1=tmpl.sum1+tmpr.sum1;
	// 需要注意的是右半部分区间长度的计算,一开始错在这里了
	// 右端点应该是当前区间最右端和查询区间最右端的最小值,具体可以举个例子画一下
	if (R>m) tmp.sum1=tmp.sum1+(min(R,t[rt].r)-m)*tmpl.sum;
	return tmp;
}

int main() {
	scanf("%d%d",&n,&q);
	build(1,n,1);
	for (int i=1;i<=q;i++) {
		scanf("%d",&op);
		if (op==2) {
			scanf("%d%lld",&p,&c);
			update(p,c,1);
		} else {
			scanf("%d%d",&l,&r);
			ans=query(l,r,1);
			printf("%lld\n",ans.sum1);
		}
	}
	return 0;
}
发布了136 篇原创文章 · 获赞 33 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Radium_1209/article/details/100147781