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 <iostream>
#include <cstdio>
#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;
}
posted @ 2022-03-09 19:58  ltdJcoder  阅读(25)  评论(0编辑  收藏  举报