[线段树练习]取模问题

操作1,求区间和,时间复杂度为 O ( log ⁡ N ) \mathcal{O}(\log N) O(logN)

操作2,区间取模,

  • 每个数被取模,数值至少减半,每个数至多被修改 log ⁡ N \log N logN 次。
  • 单个数被有效取模1次只会在线段树上访问 log ⁡ N \log N logN次。
  • 因此,该操作时间复杂度为 O ( log ⁡ N log ⁡ N ) O(\log N\log N) O(logNlogN)
  • 当x>区间最大值时,直接回溯。

操作3,单点修改,时间复杂度为 O ( log ⁡ N ) O(\log N) O(logN)

时间复杂度 O ( m log ⁡ N log ⁡ N ) O(m\log N\log N) O(mlogNlogN)

#include<bits/stdc++.h>
using namespace std;
int n,m;
long long a[100010];
struct str
{
	int l,r;//左右节点的编号 
	long long maxx,sum;//l到 r的最大值,区间和 
}t[400040]; //4倍数组 
void build(int p,int l,int r)//建树 
{
	t[p].l=l;t[p].r=r;//节点p代表区间[l,r] 
	if(l==r)
	{
		t[p].maxx=t[p].sum=a[l];
		return;
	}//给叶子结点赋初值 
	int mid=(l+r)>>1;//折半 
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);//递归左右子节点 
	t[p].maxx=max(t[p<<1].maxx,t[p<<1|1].maxx);
	t[p].sum=t[p<<1].sum+t[p<<1|1].sum;//从下往上更新区间和,区间最大值 
}
void change1(int p,int l,int r,long long x)//区间取模 
{
	if(x>t[p].maxx) return;
	//当x大于区间最大值,对每个数取模后数值不再改变,直接回溯 
	if(t[p].l==t[p].r)
	{
		t[p].maxx%=x;
		t[p].sum%=x;
		return;
	}//递归到叶子结点,取模 
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid) change1(p<<1,l,r,x);
	if(mid<r) change1(p<<1|1,l,r,x);//递归 
	t[p].maxx=max(t[p<<1].maxx,t[p<<1|1].maxx);
	t[p].sum=t[p<<1].sum+t[p<<1|1].sum;//从下往上更新区间和,区间最大值 
}
void change2(int p,int x,long long y)//单点修改 
{
	if(t[p].l==t[p].r)
	{
		t[p].maxx=t[p].sum=y;
		return;
	}//修改节点 
	int mid=(t[p].l+t[p].r)>>1;
	if(x<=mid) change2(p<<1,x,y);
	else change2(p<<1|1,x,y);//递归 
	t[p].maxx=max(t[p<<1].maxx,t[p<<1|1].maxx);
	t[p].sum=t[p<<1].sum+t[p<<1|1].sum;//从下往上更新区间和,区间最大值 
}
long long ask(int p,int l,int r)//区间求和 
{
	if(l<=t[p].l&&t[p].r<=r)
		return t[p].sum;//完全包含 
	int mid=(t[p].l+t[p].r)>>1;
	long long ans=0;//表示区间和 
	if(l<=mid) ans+=ask(p<<1,l,r);//左子节点有重叠 
	if(mid<r) ans+=ask(p<<1|1,l,r);//右子节点有重叠 
	return ans;
}
int main()
{
	freopen("mod.in","r",stdin);
	freopen("mod.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);//输入 
	build(1,1,n);//建树 
	while(m--)
	{
		int o,l,r;
		long long x;
		scanf("%d%d%d",&o,&l,&r);
		if(o==1) printf("%lld\n",ask(1,l,r));//查询区间 l到 r的和 
		if(o==2) scanf("%lld",&x),change1(1,l,r,x);//将区间 l到 r的数对 x取模 
		if(o==3) change2(1,l,r);//将 l改为 r 
	}
	return 0;
}
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值