CF438D The Child and Sequence
阿新 • • 發佈:2022-03-09
傳送門
題意
維護一個支援區間取模,區間求和,單點修改的資料結構。
題解
考慮一個數被取模後至少變為原來的\(\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)\)
實現
#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; }