Codeforces438D 线段树取模

本文介绍了一种基于线段树的数据结构实现方法,用于处理单点修改、区间求和及区间模运算等问题。通过维护区间最大值,使得每个数的有效模次数不超过 log 次,从而将复杂度降低到 nlogn。文中详细阐述了建立线段树、区间修改、单点修改以及区间查询等核心操作的具体实现。

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

题意

单点修改, 区间求和, 区间模(不对整体和模, 对每一个数模).

题解

考虑每一个数被模, 如果模数大于它, 就不管. 如果模数p小于它, 则这个数每次被模过一次就缩小至少一半. 证明: 设数为x, 模数p > x/2, 则模一次肯定小于x之后肯定x/2.  若p < x/2, 那么模p后x < p, 所以后来x < x/2. 那么一个数最多被有效模log次. 一共nlogn次即可. 单点修改和全歼求和就很简单了. 区间要多存一个区间最大值. 因为有效模最多模log次, 如果模数大于自己的话就不用再去模了. 
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 100005;
typedef long long dnt;
int a[maxn], n, m;
inline const int read(){
	register int x = 0;
	register char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return x;
}
struct node{
	node *ls, *rs;
	dnt sum; int cmax;
	void update(){
		cmax = max(ls->cmax, rs->cmax);
		sum = ls->sum + rs->sum;
	}
}pool[maxn* 4], *tail = pool, *root;
node* build(int lf, int rg){
	node *bt = ++tail;
	if(lf == rg) {bt->sum = bt->cmax = a[lf]; return bt;}
	int mid = (lf + rg) >> 1;
	bt->ls = build(lf, mid), bt->rs = build(mid + 1, rg);
	bt->update(); return bt;
}
void modify(node *bt, int lf, int rg, int L, int R, int delta){
	if(lf == rg){
		bt->sum %= delta, bt->cmax %= delta;
		return; 
	}
	int mid = (lf + rg) >> 1;
	if(L <= mid && delta <= bt->ls->cmax) modify(bt->ls, lf, mid, L, R, delta);
	if(R > mid && delta <= bt->rs->cmax) modify(bt->rs, mid + 1, rg, L, R, delta);
	bt->update();
}
void modipoint(node *bt, int lf, int rg, int pos, int delta){
	if(lf == rg){
		bt->sum = delta, bt->cmax = delta;
		return; 
	}
	int mid = (lf + rg) >> 1;
	if(pos <= mid) modipoint(bt->ls, lf, mid, pos, delta);
	else modipoint(bt->rs, mid + 1, rg, pos, delta);
	bt->update();
}
dnt query(node *bt, int lf, int rg, int L, int R){
	if(L <= lf && rg <= R) return bt->sum;
	int mid = (lf + rg) >> 1; dnt rt = 0;
	if(L <= mid) rt += query(bt->ls, lf, mid, L, R);
	if(R > mid) rt += query(bt->rs, mid + 1, rg, L, R);
	bt->update();
	return rt;
}
int main(){
	freopen("mod.in", "r", stdin);
	freopen("mod.out", "w", stdout);
	n = read(), m = read();
	for(register int i = 1; i <= n; ++i) a[i] = read();
	root = build(1, n);
	for(register int i = 1; i <= m; ++i){
		int opt = read(), l = read(), r = read(), x; 
		if(opt == 1) printf("%I64d\n", query(root, 1, n, l, r));
		if(opt == 2) x = read(), modify(root, 1, n, l, r, x);
		if(opt == 3) modipoint(root, 1, n, l, r);
	}
}
/*
5 5
1 2 3 4 5
2 3 5 4
3 3 5
1 2 5
2 1 3 3
1 1 3
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值