2020.02.11日常总结

Codeforces   48D \color{green}{\text{Codeforces\ \ \ 48D}} Codeforces   48D

【 题 目 大 意 】 : \color{blue}{【题目大意】:} 给定一个序列 A A A,有以下三种操作:

  • 询问区间 [ l , r ] [l,r] [l,r]的总和
  • 区间 [ l , r ] [l,r] [l,r]内的所有数同时对 x x x取模
  • 把第 l l l个数修改成 x x x

记序列长为 N N N,操作个数为 M M M,则 1 ≤ N , M ≤ 1 × 1 0 5 1 \leq N,M \leq 1 \times 10^5 1N,M1×105

【 思 路 】 : \color{blue}{【思路】:} 让我们先来看看取模的性质: a m o d    b ≤ ⌊ a 2 ⌋ a\mod b \leq \lfloor \dfrac{a}{2} \rfloor amodb2a

我们现在来尝试证明它:

  • b ≤ ⌊ a 2 ⌋ b \leq \lfloor \dfrac{a}{2} \rfloor b2a时,因为 a m o d    b ≤ b a\mod b \leq b amodbb,所以肯定有 a m o d    b ≤ ⌊ a 2 ⌋ a\mod b \leq \lfloor \dfrac{a}{2} \rfloor amodb2a
  • b > ⌊ a 2 ⌋ b > \lfloor \dfrac{a}{2} \rfloor b>2a时,因为 a m o d    b = a − b × ⌊ a b ⌋ a \mod b=a- b \times \lfloor \dfrac{a}{b} \rfloor amodb=ab×ba,所以也有 a m o d    b ≤ ⌊ a 2 ⌋ a\mod b \leq \lfloor \dfrac{a}{2} \rfloor amodb2a

有了这个限制,我们发现用时最长的取模操作会很快就会失去其执行的意义,即如果一个区间对 m o d mod mod取模但区间内所有的数都小于 m o d mod mod,这个取模操作是不必执行的。

既然如此,我们就随便维护区间的最大值和总和,用最普通的方法也不会TLE,只要觉得当区间最大值都小于 m o d mod mod时,不需进行取模操作(因为所有的数字都非负)。

【 代 码 】 : \color{blue}{【代码】:}

const int N=1e5+100;
typedef long long ll;
int n,m,a[N],opt,l,r;
struct Segment_tree{
	ll Max[N<<2],sum[N<<2];
	void pushup(int o){
		Max[o]=max(Max[o<<1],Max[o<<1|1]);
		sum[o]=sum[o<<1]+sum[o<<1|1];
	}
	void build(int o,int l,int r){
		if (l==r){
			Max[o]=sum[o]=a[l];
			return;
		}
		register int mid=(l+r)>>1;
		build(o<<1,l,mid);
		build(o<<1|1,mid+1,r);
		pushup(o);return;
	}
	void updata1(int o,int l,int r,int p,int q,ll mod){
		if (Max[o]<mod) return;
		if (l==r){
			Max[o]=sum[o]=sum[o]%mod;
			return;
		}
		register int mid=(l+r)>>1;
		if (p<=mid) updata1(o<<1,l,mid,p,q,mod);
		if (q>mid) updata1(o<<1|1,mid+1,r,p,q,mod);
		pushup(o);return;
	}
	void updata2(int o,int l,int r,int p,ll v){
		if (l==r){
			Max[o]=sum[o]=v;
			return;
		}
		register int mid=(l+r)>>1;
		if (p<=mid) updata2(o<<1,l,mid,p,v);
		if (p>mid) updata2(o<<1|1,mid+1,r,p,v);
		pushup(o);return;
	}
	ll query(int o,int l,int r,int p,int q){
		if (l>q||r<p) return 0ll;
		if (p<=l&&r<=q) return sum[o];
		register int mid=(l+r)>>1;
		register ll answer=0ll;
		answer+=query(o<<1,l,mid,p,q);
		answer+=query(o<<1|1,mid+1,r,p,q);
		return answer;
	}
}Seg;
int main(){
//	freopen("t1.in","r",stdin);
	n=read();m=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	Seg.build(1,1,n);
	for(int i=1;i<=m;i++){
		opt=read();l=read();r=read();
		if (opt==1) printf("%lld\n",Seg.query(1,1,n,l,r));
		else if (opt==2) Seg.updata1(1,1,n,l,r,read());
		else Seg.updata2(1,1,n,l,r);
	}
	return 0;
}

【 小 结 】 : \color{blue}{【小结】:} 我们要善于发现题目中操作的特殊性质,并且好好对它进行利用,这样,才能很好地把题做好!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值