线段树维护区间(平方和,立方和)修改区间(加,赋值,乘)

本文介绍了如何使用线段树数据结构有效地维护区间内的平方和与立方和,并详细阐述了如何进行区间加、赋值和乘等操作,提升区间查询与更新的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目地址

/*
* @Author: hannibal
* @Date:   2018-08-07 10:42:26
* @Last Modified by:   hannibal
* @Last Modified time: 2018-08-07 17:08:44
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long  ll;
const ll mod = 10007;
const ll maxn = 1e5+10;
ll Case = 1, n, m;
struct node{
	ll l, r;
	ll add, mul, set;
	ll p1, p2, p3;
	ll mid(){return (l+r)/2;}
}tr[maxn<<2];
void pushup(ll rt) {
	tr[rt].p1 = (tr[rt<<1].p1 + tr[rt<<1|1].p1)%mod;
	tr[rt].p2 = (tr[rt<<1].p2 + tr[rt<<1|1].p2)%mod;
	tr[rt].p3 = (tr[rt<<1].p3 + tr[rt<<1|1].p3)%mod;
}
void caladd(ll rt) {
	ll len1 = (tr[rt<<1].r-tr[rt<<1].l+1)%mod;
	ll len2 =( tr[rt<<1|1].r-tr[rt<<1|1].l+1)%mod;
	tr[rt<<1].add = (tr[rt<<1].add+tr[rt].add%mod);tr[rt<<1|1].add = (tr[rt<<1|1].add+tr[rt].add)%mod;
	ll temp = (tr[rt].add*tr[rt].add%mod)*tr[rt].add%mod;
	tr[rt<<1].p3 =(tr[rt<<1].p3+(temp*len1)%mod+3*tr[rt].add*((tr[rt<<1].p2+tr[rt<<1].p1*tr[rt].add)%mod)%mod)%mod;
	tr[rt<<1|1].p3 = (tr[rt<<1|1].p3+(temp*len2)%mod+3*tr[rt].add*((tr[rt<<1|1].p2+tr[rt<<1|1].p1*tr[rt].add)%mod)%mod)%mod;
	tr[rt<<1].p2 = (tr[rt<<1].p2+(tr[rt].add*tr[rt].add%mod*len1%mod)+2*tr[rt<<1].p1*tr[rt].add%mod)%mod;
	tr[rt<<1|1].p2 = (tr[rt<<1|1].p2+(tr[rt].add*tr[rt].add%mod*len2%mod)+2*tr[rt<<1|1].p1*tr[rt].add%mod)%mod;
	tr[rt<<1].p1 = (tr[rt<<1].p1+len1*tr[rt].add%mod)%mod;
	tr[rt<<1|1].p1 = (tr[rt<<1|1].p1+len2*tr[rt].add%mod)%mod;
	tr[rt].add = 0;
}
void calmul(ll rt) {
	ll x = tr[rt].mul;
	tr[rt<<1].mul = (x*tr[rt<<1].mul)%mod;
	tr[rt<<1|1].mul = (x*tr[rt<<1|1].mul)%mod;
	if(tr[rt<<1].add) tr[rt<<1].add = (tr[rt<<1].add*x)%mod;
	if(tr[rt<<1|1].add) tr[rt<<1|1].add = (tr[rt<<1|1].add*x)%mod;
	ll temp = (x*x)%mod*x%mod;
	tr[rt<<1].p1 = (tr[rt<<1].p1*x)%mod;
	tr[rt<<1|1].p1 = (tr[rt<<1|1].p1*x)%mod;
	tr[rt<<1].p2 = (tr[rt<<1].p2*x%mod*x)%mod;
	tr[rt<<1|1].p2 = (tr[rt<<1|1].p2*x%mod*x)%mod;
	tr[rt<<1].p3 = (tr[rt<<1].p3*temp%mod)%mod;
	tr[rt<<1|1].p3 = (tr[rt<<1|1].p3*temp%mod)%mod;
	tr[rt].mul = 1;
}
void calset(ll rt) {
	ll len1 = (tr[rt<<1].r-tr[rt<<1].l+1)%mod;
	ll len2 = (tr[rt<<1|1].r-tr[rt<<1|1].l+1)%mod;
	tr[rt<<1].set = tr[rt<<1|1].set = tr[rt].set;
	tr[rt<<1].add = tr[rt<<1|1].add = 0;
	tr[rt<<1].mul = tr[rt<<1|1].mul = 1;
	ll temp = (tr[rt].set*tr[rt].set)%mod*tr[rt].set%mod;
	tr[rt<<1].p1 = len1*(tr[rt].set%mod)%mod;
	tr[rt<<1|1].p1 = len2*(tr[rt].set%mod)%mod;
	tr[rt<<1].p2 = len1*(tr[rt].set*tr[rt].set%mod)%mod;
	tr[rt<<1|1].p2 = len2*(tr[rt].set*tr[rt].set%mod)%mod;
	tr[rt<<1].p3 = len1*temp%mod;
	tr[rt<<1|1].p3 = len2*temp%mod;
	tr[rt].set = 0;
}
void pushdown(ll rt) {
	if(tr[rt].set) calset(rt);
	if(tr[rt].mul != 1) calmul(rt);
	if(tr[rt].add) caladd(rt);
}
void build(ll rt, ll l, ll r){
	tr[rt].l = l; tr[rt].r = r;tr[rt].mul = 1;
	tr[rt].add =  tr[rt].set = 0;
	if(l == r) {tr[rt].p1 = tr[rt].p2 = tr[rt].p3 = 0;return;}
	ll mid = tr[rt].mid();
	build(rt<<1, l, mid);build(rt<<1|1, mid+1, r);
	pushup(rt);
}
void update(ll rt, ll l, ll r, ll flag, ll c) {
	if(tr[rt].l == l && tr[rt].r == r) {
		if(flag == 1){
			tr[rt].add += c;
			ll temp = c*c%mod*c%mod*(r-l+1)%mod;
			tr[rt].p3 = (tr[rt].p3+temp+3*c*((tr[rt].p2+tr[rt].p1*c)%mod))%mod;
			tr[rt].p2 = (tr[rt].p2+(c*c%mod*(r-l+1)%mod)+2*tr[rt].p1*c%mod)%mod;
			tr[rt].p1 = (tr[rt].p1+(r-l+1)*c%mod)%mod;
		}
		else if(flag == 2) {
			tr[rt].mul = (tr[rt].mul*c)%mod;
			if(tr[rt].add) tr[rt].add = (tr[rt].add*c)%mod;
			tr[rt].p1 = (tr[rt].p1*c)%mod;
			tr[rt].p2 = (tr[rt].p2*c%mod*c)%mod;
			tr[rt].p3 = (tr[rt].p3*c%mod*c%mod*c)%mod;
		}
		else if(flag == 3) {
			tr[rt].set = c; tr[rt].add = 0; tr[rt].mul = 1;
			tr[rt].p1 = (r-l+1)%mod*c%mod;
			tr[rt].p2 = (r-l+1)%mod*c%mod*c%mod;
			tr[rt].p3 = (r-l+1)%mod*c%mod*c%mod*c%mod;
		}
		return;
	}
	pushdown(rt);
	ll mid = tr[rt].mid();
	if(r <= mid) update(rt<<1, l, r, flag, c);
	else if(l > mid) update(rt<<1|1, l, r, flag, c);
	else update(rt<<1, l, mid, flag, c), update(rt<<1|1, mid+1, r, flag, c);
	pushup(rt);
}
ll query(ll rt, ll l, ll r, ll p) {
	if(tr[rt].l == l && tr[rt].r == r) {
		if(p == 1) return tr[rt].p1%mod;
		else if(p == 2) return tr[rt].p2%mod;
		else return tr[rt].p3%mod;
	}
	pushdown(rt);
	ll mid = tr[rt].mid();
	if(r <= mid) return query(rt<<1, l, r, p);
	else if(l > mid) return query(rt<<1|1, l, r, p);
	else return (query(rt<<1, l, mid, p)+query(rt<<1|1,mid+1, r, p)+mod)%mod;
}
void solve() {
	build(1, 1, n);
	for(ll i = 1; i <= m; i++){
		ll q, x, y, p;
		scanf("%llu%llu%llu%llu", &q, &x, &y, &p);
		if(q == 4) printf("%llu\n", query(1, x, y, p)%mod);
		else update(1, x, y, q, p);
	}
}
int main() {
    ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    while(scanf("%llu%llu", &n, &m) == 2&&(n+m)) {
        solve();
    }
    return 0;
}
<think>嗯,用户想知道如何使用线段树来计算区间平方和,并且需要高效的方法代码示例。首先,我得回忆一下线段树的基本结构。线段树每个节点保存区间信息,比如区间、最值之类的。那平方和的话,可能需要维护额外的信息。 首先,常规的线段树可能只维护区间,但平方和需要每个元素的平方和。不过,如果涉及到区间更新,比如给区间里的每个元素上一个值,这时候平方和的计算会更复杂。因为每个元素的变化会影响平方和,比如(a + x)^2 = a² + 2ax + x²,所以整个区间平方和会增2xΣa + x²*length。这时候可能需要维护普通平方和两个值。 用户的问题可能是在区间修改的情况下如何高效计算平方和。比如,当区间一个数时,如何快速更新平方和。这时候线段树的节点需要存储哪些信息呢?应该需要存储sum(普通sqr_sum(平方和),以及可能的延迟标记add,记录待下传的增量。 当进行区间操作时,假设当前节点的区间长度为len,原来的sum是S,sqr是Q。一个值x后,新的sum是S + x*len。新的sqr_sum应该是原来的Q + 2x*S + x²*len。所以在更新的时候,需要同时更新这两个值,并且处理延迟标记。 然后,合并子节点的时候,父节点的sum是左右子节点的sum之,sqr_sum也是左右子节点的sqr_sum之。这样在构建查询时,就可以正确计算。 接下来,代码实现的话,线段树的每个节点需要包含sum、sqr_sumadd。构建的时候,叶子节点初始化sumsqr_sum为数组的值平方。对于内部节点,合并左右子节点的sumsqr_sum。 在push_down操作中,需要将add标记传递给子节点,并更新子节点的sumsqr_sum。例如,左子节点原来的sum是sum_l,add有p的值,那么当传递一个add_val时,新的sum_l会是sum_l + add_val * len_l。而sqr_l则会是原来的sqr_l + 2*add_val*sum_l + add_val² * len_l。同时,子节点的add标记需要累,而不是直接替换,因为可能有多个未下传的更新。 区间查询的时候,就是标准的线段树查询方法,将查询区间分解到各个节点,累它们的sqr_sum。 现在需要验证这个逻辑是否正确。比如,假设有一个区间[1,2,3],sum是6,sqr_sum是1+4+9=14。如果对这个区间2,那么每个元素变成3,4,5,新的sum是12,sqr_sum是9+16+25=50。根据公式,原来的sqr_sum是14,新的应该是14 + 2*2*6 + 2²*3 = 14 +24 +12=50,正确。 所以,这样的维护方法是可行的。接下来需要编写代码示例,结构包括构建、更新、查询的函数。Python的话可以用类来实现,每个节点有左右边界、sum、sqr_sum、add。或者用数组来存储,但可能比较麻烦,所以用递归的方式更直观。 另外,需要考虑区间更新的延迟传播,也就是在更新查询的时候,如果有未处理的add标记,需要先下传给子节点。这样确保每次操作的准确性。 最后,用户可能需要具体的代码示例,所以给出一个线段树的实现,包括初始化、更新区间操作、查询区间平方和的方法。并给出测试用例,比如数组初始值,进行一些更新查询,验证结果是否正确。</think>### 使用线段树高效计算区间平方和的方法 #### 核心思路 线段树节点需要维护以下信息: 1. **区间**($sum$):用于计算平方和的中间值 2. **区间平方和**($sqr\_sum$):直接返回查询结果 3. **延迟标记**($add$):处理区间增量更新的关键 对于区间增量更新值$v$,数学推导如下: $$new\_sqr\_sum = old\_sqr\_sum + 2v \cdot sum + v^2 \cdot len$$ 其中$len$为区间长度。此公式保证了时间复杂度仍为$O(\log N)$[^3]。 #### 代码实现(Python) ```python class SegmentTree: def __init__(self, data): self.n = len(data) self.tree = [{} for _ in range(4 * self.n)] self._build(0, 0, self.n-1, data) def _build(self, node, l, r, data): self.tree[node] = {'l': l, 'r': r, 'sum': 0, 'sqr_sum': 0, 'add': 0} if l == r: self.tree[node]['sum'] = data[l] self.tree[node]['sqr_sum'] = data[l] ** 2 return mid = (l + r) // 2 self._build(2*node+1, l, mid, data) self._build(2*node+2, mid+1, r, data) self._push_up(node) def _push_up(self, node): left = 2*node+1 right = 2*node+2 self.tree[node]['sum'] = self.tree[left]['sum'] + self.tree[right]['sum'] self.tree[node]['sqr_sum'] = self.tree[left]['sqr_sum'] + self.tree[right]['sqr_sum'] def _push_down(self, node): if self.tree[node]['add'] == 0: return left = 2*node+1 right = 2*node+2 add = self.tree[node]['add'] # 更新左子树 len_l = self.tree[left]['r'] - self.tree[left]['l'] + 1 self.tree[left]['sqr_sum'] += 2 * add * self.tree[left]['sum'] + add**2 * len_l self.tree[left]['sum'] += add * len_l self.tree[left]['add'] += add # 更新右子树 len_r = self.tree[right]['r'] - self.tree[right]['l'] + 1 self.tree[right]['sqr_sum'] += 2 * add * self.tree[right]['sum'] + add**2 * len_r self.tree[right]['sum'] += add * len_r self.tree[right]['add'] += add self.tree[node]['add'] = 0 def range_add(self, l, r, val): self._update(0, l, r, val) def _update(self, node, ql, qr, val): l = self.tree[node]['l'] r = self.tree[node]['r'] if qr < l or r < ql: return if ql <= l and r <= qr: self._apply_update(node, val) return self._push_down(node) self._update(2*node+1, ql, qr, val) self._update(2*node+2, ql, qr, val) self._push_up(node) def _apply_update(self, node, val): len_node = self.tree[node]['r'] - self.tree[node]['l'] + 1 self.tree[node]['sqr_sum'] += 2 * val * self.tree[node]['sum'] + val**2 * len_node self.tree[node]['sum'] += val * len_node self.tree[node]['add'] += val def query_sqr_sum(self, l, r): return self._query(0, l, r) def _query(self, node, ql, qr): l = self.tree[node]['l'] r = self.tree[node]['r'] if qr < l or r < ql: return 0 if ql <= l and r <= qr: return self.tree[node]['sqr_sum'] self._push_down(node) return self._query(2*node+1, ql, qr) + self._query(2*node+2, ql, qr) # 测试示例 arr = [1, 2, 3] st = SegmentTree(arr) print(st.query_sqr_sum(0, 2)) # 输出14 (1²+2²+3²=14) st.range_add(0, 2, 2) # 每个元素+2 print(st.query_sqr_sum(0, 2)) # 输出50 (3²+4²+5²=50) ``` #### 关键操作说明 1. **初始化**:构建时计算初始平方和 2. **区间更新**:通过延迟标记保证时间复杂度 3. **结果查询**:利用预存平方和快速响应 4. **标记下传**:确保子节点数据及时更新[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值