zoukankan      html  css  js  c++  java
  • Be Geeks!

    传送门
    (sum_{i = 1} ^ nsum_{j = i}^n max(a_i, ..., a_j) * gcd(a_i, ... , a_j))

    对于每一个(a_i),都有一段区间,表示在这一段区间里,(a_i)是最大的,也就是说,max值是一样的,那么只需要算出这一段区间里的子gcd和,再乘以(a_i),就是这一段的贡献值。

    因为是对于gcd来说的,在求所有数的gcd时,每次加入一个(a[i]),就会使得gcd不变或者变小。而ST也是如此,那么可以利用ST表来维护所有区间的gcd值。
    然后对于每一个查询区间,固定以端点,进行二分查找。找到gcd与当前区间不同的位置。
    那么线段树维护下每一个(a_i)所在的区间,然后进行区间加 + 区间和查询即可。
    每次找到新的gcd就把这段gcd乘以这段区间的最值累积加起来即可。

    ST表的常数优化真香

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #define ll long long
    using namespace std;
    const int N = 2e5 + 5;
    const int mod = 1e9 + 7;
    struct SegTree{
        struct Tree{
            int l, r, val;
            int lazy;
            #define l(p) tree[p].l
            #define r(p) tree[p].r
            #define val(p) tree[p].val
            #define lazy(p) tree[p].lazy
            #define lson p << 1
            #define rson p << 1 | 1
        } tree[N << 2];
        void pushup(int p){
            val(p) = (val(lson) + val(rson)) % mod;
        }
        void pushdown(int p){
            if(lazy(p)) {
                lazy(lson) = lazy(p);
                lazy(rson) = lazy(p);
                val(lson) = 1ll * lazy(p) * (r(lson) - l(lson) + 1) % mod;
                val(rson) = 1ll * lazy(p) * (r(rson) - l(rson) + 1) % mod; 
                lazy(p) = 0;
            }
        }
        void build(int p, int l, int r){
            l(p) = l, r(p) = r; lazy(p) = val(p) = 0;
            if(l == r) {
                return ;
            }
            int mid = (l + r) >> 1;
            build(lson, l, mid);
            build(rson, mid + 1, r);
            pushup(p);
        }
        void change(int p, int l, int r, int x){
            if(l <= l(p) && r(p) <= r) {
                val(p) = 1ll * x * (r(p) - l(p) + 1) % mod;
                lazy(p) = x;
                return;
            }
            pushdown(p);
            int mid = (l(p) + r(p)) >> 1;
            if(l <= mid) change(lson, l, r, x);
            if(r > mid) change(rson, l, r, x);
            pushup(p);
        }
        int query(int p, int l, int r){
            if(l <= l(p) && r(p) <= r) {
                return val(p);
            }
            pushdown(p);
            int mid = (l(p) + r(p)) >> 1;
            int ans = 0;
            if(l <= mid) ans = (ans + query(lson, l, r)) % mod;
            if(r > mid) ans = (ans + query(rson, l, r)) % mod;
            return ans; 
        }
    } seg; // 区间加法 + 区间和查询
    
    int lans[N], lg[N], stk[N], a[N], top, n;
    int st[N][20];
    int gcd(int a, int b){
        return b == 0 ? a : gcd(b, a % b);
    }
    void init(){
        top = 0;
        lg[1] = 0;
        for(int i = 2;i <= n; i++) lg[i] = lg[i >> 1] + 1;
        for(int i = 1; i <= n; i++) {
            while(top && a[stk[top]] < a[i]) top--;
            lans[i] = (!top ? 0 : stk[top]) + 1;
            stk[++top] = i;
        }
        for(int i = 1; i <= n; i++) st[i][0] = a[i]; 
        int k = log2(n / 2) + 1;
        for(int j = 1; j <= k; j++)
            for(int i = 1; i + (1 << j) - 1 <= n; i++)
                st[i][j] = gcd(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
    }
    int query(int l, int r){ //ST表区间查询gcd
        int k = lg[r - l + 1];
        return gcd(st[l][k], st[r - (1 << k) + 1][k]);
    }
    ll ans = 0;
    void work(int x, int ups){ // 分段求gcd 再乘以系数
        if(x <= 0)return;
        int l = 1, r = x, stdd = query(x, ups);
        while(l < r){
            int mid = (l + r) >> 1;
            if(query(mid, ups) == stdd) r = mid;
            else l = mid + 1;
        }
        ans = (ans + 1ll * seg.query(1, l, x) * stdd) % mod;
        work(l - 1, ups);
    }
    namespace IO {
        template <typename T>
        inline void w(T x) { if (x > 9) w(x / 10); putchar(x % 10 + 48); }
        template <typename T>
        inline void write(T x, char c) { if(x < 0) putchar('-'), x = -x;w(x); putchar(c); }
        template <typename T>
        inline void read(T &x) {
            x = 0; T f = 1; char c = getchar();
            for (; !isdigit(c); c = getchar()) if (c == '-') f = -1;
            for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
            x *= f;
        }
    };
    int main(){
        IO::read(n);
        seg.build(1, 1, n);
        for(int i = 1; i <= n; i++) IO::read(a[i]);
        init();
        for(int i = 1; i <= n; i++) {
            seg.change(1, lans[i], i, a[i]); // 每次更新以i为最大值的区间,加上a[i];
            work(i, i);
        }
        IO::write(ans, '
    ');
        return 0;
    }
    
  • 相关阅读:
    微信小程序の模板
    微信小程序の条件渲染
    微信小程序のwxml列表渲染
    769. Max Chunks To Make Sorted
    766. Toeplitz Matrix
    747. Largest Number At Least Twice of Others
    746. Min Cost Climbing Stairs
    729. My Calendar I
    724. Find Pivot Index
    718. Maximum Length of Repeated Subarray
  • 原文地址:https://www.cnblogs.com/Emcikem/p/13764618.html
Copyright © 2011-2022 走看看