CodeFouces 438D The Child and Sequence

题目大意

%   维护一个长度为 n n n 的序列,完成 m m m个操作,区间取模,区间求和,单点修改。
  数据范围  1 ⩽ n ⩽ 1 0 5 , 1 ⩽ m ⩽ 1 0 5 1\leqslant n\leqslant 10^5,1\leqslant m\leqslant 10^5 1n105,1m105

题解

%   考虑用线段树修改,每次区间修改时暴力修改,记录一下最大值,若小于模数直接返回。
  然后你会发现,这样就可以通过本题了。为什么?
  引理 当 p &lt; x p&lt;x p<x 时, x &VeryThinSpace; m o d &VeryThinSpace; p &lt; x 2 x\bmod p&lt; \dfrac x2 xmodp<2x
  证明 若 p &lt; x 2 p&lt;\dfrac x2 p<2x,则 x &VeryThinSpace; m o d &VeryThinSpace; p &lt; p &lt; x 2 x\bmod p&lt;p&lt; \dfrac x2 xmodp<p<2x
     若 x 2 &lt; p &lt; x \dfrac x2&lt;p&lt;x 2x<p<x,则 x &VeryThinSpace; m o d &VeryThinSpace; p = x − p &lt; x 2 x\bmod p=x-p&lt; \dfrac x2 xmodp=xp<2x
  因此,每次取模的时,若某个位置的数小于模数,则不需要修改,若大于模数,每次至少减半,因此之多被取模 log ⁡ 2 a i \log_2 a_i log2ai 次,因而总时间复杂度为 T ( n ) = Θ ( n log ⁡ 2 2 n ) T(n)=\Theta(n\log_2^2 n) T(n)=Θ(nlog22n)

#include<bits/stdc++.h>
using namespace std;
struct tree{
	int l,r;
	long long d,maxx;
	tree *left,*right;
	tree(int tl,int tr):l(tl),r(tr){
		if(tl==tr){
			left=right=NULL;
			d=maxx=0;
			return ;
		}
		int mid=(l+r)>>1;
		left=new tree(l,mid);
		right=new tree(mid+1,r);
	}
	void change(int x,long long da){
		if(l==r){
			maxx=d=da;
			return;
		}
		if(x<=left->r) left->change(x,da);
		else right->change(x,da);
		maxx=max(left->maxx,right->maxx);
		d=left->d+right->d;
	}
	void mod(int x,int y,long long p){
		if(maxx<p)//不需要继续
			return;
		if(l==r){//暴力取模
			maxx%=p;
			d%=p;
			return;
		}
		if(y<=left->r) left->mod(x,y,p);
		else if(x>=right->l) right->mod(x,y,p);
		else left->mod(x,left->r,p),right->mod(right->l,y,p);
		maxx=max(left->maxx,right->maxx);
		d=left->d+right->d;
	}
	long long sum(int x,int y){
		if(x==l&&r==y)
			return d;
		if(y<=left->r)
			return left->sum(x,y);
		if(x>=right->l)
			return right->sum(x,y);
		return left->sum(x,left->r)+right->sum(right->l,y);
	}
};
int main(void){
	int n,m;
	scanf("%d%d",&n,&m);
	tree t(1,n);
	for(int i=1,x;i<=n;i++){
		scanf("%d",&x);
		t.change(i,x);
	}
	for(int i=1,ch,l,r,x;i<=m;i++){
		scanf("%d",&ch);
		if(ch==1){
			scanf("%d%d",&l,&r);
			printf("%lld\n",t.sum(l,r));
		}else if(ch==2){
			scanf("%d%d%d",&l,&r,&x);
			t.mod(l,r,x);
		}else{
			scanf("%d%d",&l,&x);
			t.change(l,x);
		}
	}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值