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;
}
I‘m Stein, welcome to my blog