Multiply game HDU - 3074 线段树

题解

给n个数字 两种操作一种将某个点修改为某个值 另一种询问一个区间的所有元素乘积

线段树模板提 单点修改区间查询

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int MAXN = 5e4 + 10;
ll a[MAXN];

struct node 
{
	int l, r;
	ll v;
}tre[MAXN * 4];

inline void PushUp(int x)
{
	tre[x].v = tre[x << 1].v * tre[x << 1 | 1].v % MOD;
}
void Build(int x, int l, int r)
{
	tre[x].l = l, tre[x].r = r, tre[x].v = 0;
	if (l == r)
		tre[x].v = a[l];
	else
	{
		int m = l + r >> 1;
		Build(x << 1, l, m);
		Build(x << 1 | 1, m + 1, r);
		PushUp(x);
	}
}
void Update(int x, int p, ll v)
{
	int l = tre[x].l, r = tre[x].r;
	if (l == r)
		tre[x].v = v;
	else
	{
		int m = l + r >> 1;
		if (m >= p)
			Update(x << 1, p, v);
		else
			Update(x << 1 | 1, p, v);
		PushUp(x);
	}
}
ll Query(int x, int pl, int pr)
{
	int l = tre[x].l, r = tre[x].r;
	if (pl <= l && r <= pr)
		return tre[x].v;
	else
	{
		int m = l + r >> 1;
		ll v = 1;
		if (m >= pl)
			v = v * Query(x << 1, pl, pr) % MOD;
		if (m < pr)
			v = v * Query(x << 1 | 1, pl, pr) % MOD;
		return v;
	}
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int T;
	cin >> T;
	while (T--)
	{
		int n, q;
		cin >> n;
		for (int i = 1; i <= n; i++)
			scanf("%d", &a[i]);
		Build(1, 1, n);
		cin >> q;
		for (int i = 0; i < q; i++)
		{
			int op, x, y;
			scanf("%d%d%d", &op, &x, &y);
			if (op)
				Update(1, x, y);
			else
				printf("%lld\n", Query(1, x, y));
		}
	}

	return 0;
}
### 乘法线段树的数据结构与算法 #### 数据结构设计 为了支持乘法更新操作,乘法线段树需要额外维护一个懒惰标记(`lazy_tag`),用于记录当前节点是否被延迟更新及其对应的乘法因子。以下是乘法线段树的典型节点结构: ```cpp struct SegmentTreeNode { int start, end; // 当前区间的起点和终点 long long product; // 当前区间的积 long long lazy_multiply; // 懒惰标记:区间乘法因子 }; ``` 在初始化时,`product` 表示当前区间的累积乘积,而 `lazy_multiply` 初始值应设置为 1,因为任何数乘以 1 不变[^4]。 --- #### 构建线段树 构建过程类似于普通线段树,但在递归过程中需注意计算子区间的乘积并将其累加到父节点的 `product` 中。以下是一个简单的构建方法: ```cpp void build(SegmentTreeNode* node, vector<int>& nums) { if (node->start == node->end) { // 叶子节点 node->product = nums[node->start]; return; } int mid = (node->start + node->end) / 2; if (!node->left) node->left = new SegmentTreeNode(node->start, mid); if (!node->right) node->right = new SegmentTreeNode(mid + 1, node->end); build(node->left, nums); // 左子树 build(node->right, nums); // 右子树 node->product = node->left->product * node->right->product; // 合并左右子树的结果 } ``` 通过上述代码可以完成线段树的初始化工作[^3]。 --- #### 更新操作(范围乘法) 当需要对某个区间 `[l, r]` 进行乘法更新时,可以通过懒惰传播机制减少不必要的递归调用。具体逻辑如下: 1. 如果当前节点完全覆盖目标区间,则直接应用乘法因子,并将该因子传递给其子节点。 2. 若部分覆盖,则继续向下递归处理左子树和右子树。 3. 在返回的过程中重新计算当前节点的 `product` 值。 下面是实现代码片段: ```cpp void push_down(SegmentTreeNode* node) { if (node->lazy_multiply != 1 && node->left && node->right) { node->left->product *= pow(node->lazy_multiply, (node->left->end - node->left->start + 1)); node->right->product *= pow(node->lazy_multiply, (node->right->end - node->right->start + 1)); node->left->lazy_multiply *= node->lazy_multiply; node->right->lazy_multiply *= node->lazy_multiply; node->lazy_multiply = 1; // 清除当前节点的懒惰标记 } } void update_range(SegmentTreeNode* node, int l, int r, long long val) { if (r < node->start || l > node->end) return; // 完全不相交 if (l <= node->start && node->end <= r) { // 完全覆盖 node->product *= pow(val, (node->end - node->start + 1)); node->lazy_multiply *= val; return; } push_down(node); // 下推懒惰标记 update_range(node->left, l, r, val); // 处理左子树 update_range(node->right, l, r, val); // 处理右子树 node->product = node->left->product * node->right->product; // 合并结果 } ``` 此代码实现了高效的范围乘法更新功能[^2]。 --- #### 查询操作(范围查询) 查询某区间 `[l, r]` 的乘积时,同样利用懒惰传播来优化性能。核心思路是对目标区间逐步分解至叶子节点,并累计各子区间的贡献值。 ```cpp long long query_range(SegmentTreeNode* node, int l, int r) { if (r < node->start || l > node->end) return 1; // 返回单位元 if (l <= node->start && node->end <= r) return node->product; // 完全覆盖 push_down(node); // 下推懒惰标记 long long res_left = query_range(node->left, l, r); long long res_right = query_range(node->right, l, r); return res_left * res_right; // 合并左右子树的结果 } ``` 以上代码能够快速获取指定区间的乘积值[^1]。 --- #### 总结 乘法线段树的核心在于引入懒惰标记以支持高效的操作。相比普通的加法线段树,它主要增加了对乘法因子的支持,并调整了相应的更新逻辑。通过合理的数据结构设计与算法实现,可以在复杂度 \(O(\log n)\) 的时间内完成单次更新或查询操作。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值