CF438D The Child and Sequence
传送门
题意
维护一个支持区间取模,区间求和,单点修改的数据结构。
题解
考虑一个数被取模后至少变为原来的\(\frac 12\)(模数小于原数的情况下), 也就是说对于每个单点修改加入的数,只会被取log次模。
证明:对于一个数 \(x\),若模数 \(p\) 小于 \(\frac x2\),显然符合,若模数大于 \(\frac x2\), 则结果等于 \(x - p > x - \frac x2\)
那么如果没有冗余操作的情况下, 暴力的复杂度是正确的。
什么是冗余操作呢,我们的性质是一个数被取很多次模之后会变的很小,我们认为很小的数不需要被取模,
实际上就是说,对于小于模数的数就可以不用操作了, 也就是说如果我们每次取模的数都大于模数那么复杂度就是正确的。
如果只是朴素的区间取模,对于一个区间中的每个数判断一下是否大于模数,那么就会有很多冗余操作。
这时候一种显然的想法是,我们可以使用排序树上二分,找到每个区间中大于模数的数,对他们取模。
复杂度为 \(O(nlog^2n)\), 取模复杂度均摊下来是 \(O(nlogn)\) 的,每次找到大于模数的数,要在排序树上找到对应区间,一共有\(O(logn)\)个块, 每个块上做二分\(O(logn)\)的, 写到这我发现这个做法是不支持修改的, 寄。
学习了题解的炫酷做法后, 被炫酷了。
只要我们能够只对所有大于模数的数取模,复杂度就正确,考虑线段树上维护某个区间的最大值,如果这个区间的最大值大于模数,那么这个区间里肯定有要被取模的数,继续递归,否则没有就直接返回。这样就可以找到所有合法的数了!对于每个数每次被修改,找到他需要经过 \(O(logn)\)个区间,一个数要被修改 \(O(logn)\) 次, 总复杂度 \(O(nlog^2n)\)
实现
#include
#include
#define l(o) (o*2)
#define r(o) (o*2+1)
#define ll long long
using namespace std;
int read(){
int num=0, flag=1; char c=getchar();
while(!isdigit(c) && c!='-') c=getchar();
if(c == '-') c=getchar(), flag=-1;
while(isdigit(c)) num=num*10+c-'0', c=getchar();
return num*flag;
}
const int N = 2e5;
ll a[N];
int n, m;
struct SegTree{
ll tree[N<<2], maxv[N<<2];
void push_up(int o){
tree[o] = tree[l(o)] + tree[r(o)];
maxv[o] = max(maxv[l(o)], maxv[r(o)]);
}
void update(int o, int l, int r, int x, int v){
if(l == r){
tree[o] = v;
maxv[o] = v;
return ;
}
int mid = (l+r)/2;
if(x <= mid) update(l(o), l, mid, x, v);
else update(r(o), mid+1, r, x, v);
push_up(o);
}
ll query(int o, int l, int r, int ql, int qr){
if(ql<=l && r<=qr) return tree[o];
ll res = 0; int mid = (l+r)/2;
if(ql <= mid) res += query(l(o), l, mid, ql, qr);
if(qr > mid) res += query(r(o), mid+1, r, ql, qr);
return res;
}
void mod(int o, int l, int r, int ql, int qr, int v){
if(maxv[o] < v) return ;
if(l == r) {
tree[o] %= v;
maxv[o] %= v;
return ;
}
int mid = (l+r)/2;
if(ql <= mid) mod(l(o), l, mid, ql, qr, v);
if(qr > mid) mod(r(o), mid+1, r, ql, qr, v);
push_up(o);
}
void update(int x, int v){update(1, 1, n, x, v);}
ll query(int l, int r){return query(1, 1, n, l, r);}
void mod(int l, int r, int v){mod(1, 1, n, l, r, v);}
}t;
int main(){
n=read(), m=read();
for(int i=1; i<=n; i++) t.update(i, read());
while(m--){
int type = read();
if(type == 1){
int l=read(), r=read();
printf("%lld\n", t.query(l, r));
}else if(type == 2){
int l=read(), r=read(), v=read();
t.mod(l, r, v);
}else{
int x=read(), v=read();
t.update(x, v);
}
}
return 0;
}