题解 CF438D The Child and Sequence

题意

有一个序列 \(a\),进行形如如下的操作:

1.格式为 1 l r,求 \(\sum_{i=l}^r a_{i}\) 输出。

2.格式为 2 l r x,对区间 \([l,r]\) 取模,模数为 \(x\)

3.格式为 3 x k,修改 \(x\)\(k\)

思路

考虑操作 \(1,3\),直接做就行了,\(1\) 操作直接是线段树模板,\(3\) 直接暴力修改即可,这样就不用考虑 \(3\)\(1\) 操作的影响。

现在还有一个问题,\(2\) 操作可能会与自己矛盾,所以考虑寻找性质。

我们知道,模一个数的结果是绝对小于模数的,所以考虑如果已经模过了一个较小的数,那么再模较大数就不会产生影响。对于一个数,考虑它的最大变换次数:

值域最大是 \(10^{9}\) 的,每次考虑贪心的让接下来可以用的模数最大,即模后的值最大。


引理

对于一个数,如果模不大于它的数,这个数绝对会小于它的一半。

证明

考虑模大于它的一半的数,那么结果是 \(x-p\),因为 \(p>\frac{2}{x}\),所以结果一定小于 \(\frac{2}{x}\),如果模小于它的一半的数,那么结果一定小于模数。如果 \(x\) 为偶数,那么模它的一半就是 \(0\),如果是奇数则不存在等于它的一半的模数。


所以不考虑修改的情况,对于一个位置的数我们最多模 \(\log 10^9\) 次。如果考虑修改,一次修改最多让我们增加 \(\log 10^9\) 次修改。加上 \(10^5\) 次询问全修改也就只会让总共模的次数小于 \(6 \times 10^6\) 次,何况不能这样,还要把询问留给模操作。

因此我们得到结论,操作 \(2\) 只需记录区间最小值来判断这个区间要不要修改,如果需要就暴力修改即可,考虑最坏情况是全都是单独的,时间复杂度 \(O(n \log V \log n)\)

其他操作正常做。

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
struct segment {
	int l,r,maxn,sum;
	#define l(x) tr[x].l
	#define r(x) tr[x].r
	#define maxn(x) tr[x].maxn
	#define sum(x) tr[x].sum
};
int a[N];
struct segment_tree {
	segment tr[N*4];
	void pu(int x) {
		sum(x)=sum(x*2)+sum(x*2+1);
		maxn(x)=max(maxn(x*2),maxn(x*2+1));
	}
	void build(int x,int l,int r) {
		l(x)=l,r(x)=r;
		if(l==r) {
			sum(x)=maxn(x)=a[l];
			return;
		}
		int mid=l+r>>1;build(x*2,l,mid);build(x*2+1,mid+1,r);pu(x);
	}
	void change(int x,int ll,int rr,int k,int id) {
		int l=l(x),r=r(x);
		if(l==r) {
			if(id==1) {
				sum(x)%=k;
				maxn(x)%=k;
			}
			else {
				sum(x)=k;
				maxn(x)=k;
			}
			return;
		}
		int mid=l+r>>1;
		if(ll<=mid&&(id==2||maxn(x*2)>=k)) change(x*2,ll,rr,k,id);
		if(mid<rr&&(id==2||maxn(x*2+1)>=k)) change(x*2+1,ll,rr,k,id);
		pu(x);
	}
	int qry(int x,int ll,int rr) {
		int l=l(x),r=r(x);
		if(l>=ll&&r<=rr) return sum(x);
		int mid=l+r>>1,sum=0;
		if(mid>=ll) sum+=qry(x*2,ll,rr);
		if(mid<rr) sum+=qry(x*2+1,ll,rr);
		return sum;
	}
}tree;
int n,m;
signed main() {
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	tree.build(1,1,n);
	while(m--) {
		int op,l,r,x;
		cin>>op;
		if(op==1) {
			cin>>l>>r;
			cout<<tree.qry(1,l,r)<<endl;
		}
		else if(op==2) {
			cin>>l>>r>>x;
			tree.change(1,l,r,x,1);
		}
		else {
			cin>>l>>x;
			tree.change(1,l,l,x,2);
		} 
	}
	return 0;
}
posted @ 2024-08-23 11:41  PM_pro  阅读(15)  评论(0)    收藏  举报