Be Geeks! - 子区间gcd和,线段树 + 单调栈 + ST表求区间gcd + 二分

传送门
\(\sum_{i = 1} ^ n\sum_{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, '\n');
    return 0;
}
posted @ 2020-10-03 16:42  Emcikem  阅读(276)  评论(0编辑  收藏  举报