The 15th Chinese Northeast Collegiate Programming Contest D题

这篇博客介绍了如何使用线段树处理一种特殊的区间修改和查询问题,即对区间内的每个数加上自身的lowbit值,并进行m次操作。博主指出,由于每次加lowbit值最多只会使数的二进制位增加logn次,因此问题转化为普通的线段树区间修改和查询。在实现过程中,需要注意在满足特定条件时才进行取模操作以避免错误。代码中展示了详细的线段树构建和操作过程。
摘要由CSDN通过智能技术生成

题目链接
大致题意:
给你一段含n个数字的序列,对于这段序列可以有m次操作。
操作有两种类型:
1、(1,L,R)表示将(L,R)区间的每个数加自身的lowbit值(若一个数为x,则其lowbit值为x&-x).
2、(2,L,R)询问区间(L,R)数字之和

思路:

一眼看上去知道要用线段树维护区间信息,但对于操作1对区间每个数都需要加上其lowbit值,似乎直接用单点修改来做会T得很惨,但是仔细一想加的值很特殊是该数的lowbit的值,我们仔细一想便会发现一个数最多加logn次其lowbit值后继续加上lowbit值就变成了乘2(因为此时该数二进制形式上只有最高位为1了)。那么我们对于每个数的单点修改操作最多也只需要进行nlogn次然后题目就变成了一个普通线段树的区间修改和区间求和问题。

所以我们只需要在建造线段树时每个节点加上一个判断是乘2还是加上lowbit的值即可。但因为题目中存在取模操作,对于加上lowbit的情况贸然进行取模可能会导致该数二进制位情况的变化,从而使得结果错误,所以我们需要在当该数还未到达直接乘2的情况时不对其进行取模,直到该数符合乘2的情况我们对该数修改时才进行取模操作。

相关代码(细节wa到疯狂):

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 998244353;
struct node {
	int l, r, sum, flag;
};
int cnt[N], lazy[N * 4];
node tr[N * 4];
int lowbit(int u) { return u & -u; }
bool check(int u) {
	if ((u + lowbit(u)) == (u + u)) return true;
	else return false;
}
void push_up(node& u, node& l, node& r) {
	if (l.flag == 1 && r.flag == 1) u.flag = 1;
	else u.flag = 0;
	u.sum = (l.sum + r.sum) % mod;
	return;
}
void push_up(int u) {
	push_up(tr[u], tr[u << 1], tr[u << 1 | 1]);
	return;
}
void build(int u, int l, int r) {
	if (l == r) {
		tr[u] = { l,l,cnt[l] };
		if (check(cnt[l])) tr[u].flag = 1;
		else tr[u].flag = 0;
		return;
	}
	tr[u] = { l,r };
	int mid = l + r >> 1;
	build(u << 1, l, mid);
	build(u << 1 | 1, mid + 1, r);
	push_up(u);
	return;
}
void push_down(int u) {
	if (lazy[u] > 1) {
		lazy[u << 1] = (lazy[u << 1] * lazy[u]) % mod;
		lazy[u << 1 | 1] = (lazy[u << 1 | 1] * lazy[u]) % mod;
		tr[u << 1].sum = (tr[u << 1].sum * lazy[u]) % mod;
		tr[u << 1 | 1].sum = (tr[u << 1 | 1].sum * lazy[u]) % mod;
		lazy[u] = 1;
		return;
	}
}
void modify(int u, int l, int r) {
	if (tr[u].l == tr[u].r) {
		if (tr[u].flag == 1) tr[u].sum = (tr[u].sum * 2) % mod;
		else {
			tr[u].sum = (tr[u].sum + lowbit(tr[u].sum));
			if (check(tr[u].sum)) tr[u].flag = 1;
		}
		return;
	}
	if (tr[u].l >= l && tr[u].r <= r && tr[u].flag == 1) {
		lazy[u] = (lazy[u] * 2) % mod;
		tr[u].sum = (tr[u].sum * 2) % mod;
		return;
	}
	push_down(u);
	int mid = tr[u].l + tr[u].r >> 1;
	if (l <= mid) modify(u << 1, l, r);
	if (r > mid) modify(u << 1 | 1, l, r);
	push_up(u);
	return;
}
int query(int u, int l, int r) {
	if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum % mod;
	push_down(u);
	int ans = 0;
	int mid = tr[u].l + tr[u].r >> 1;
	if (l <= mid) ans = (ans + query(u << 1, l, r)) % mod;
	if (r > mid) ans = (ans + query(u << 1 | 1, l, r)) % mod;
	return ans % mod;
}
signed main() {
	int t; scanf("%lld", &t);
	while (t--) {
		int n; scanf("%lld", &n);
		for (int i = 1; i <= n; i++) scanf("%lld", &cnt[i]);
		for (int i = 1; i <= 4 * n; i++) lazy[i] = 1, tr[i].sum = 0, tr[i].flag = 0;
		build(1, 1, n);
		int m; scanf("%lld", &m);
		while (m--) {
			int op, l, r; scanf("%lld %lld %lld", &op, &l, &r);
			if (op == 1)
				modify(1, l, r);
			else if (op == 2) {
				int ans = query(1, l, r) % mod;
				printf("%lld\n", ans);
			}
		}
	}
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值