GCD on Sequence
我们可以发现,假设区间 [ l , r ] [l,r] [l,r] 内 v ( l , r ) = d v(l,r)=d v(l,r)=d ,那么 [ l , r ] [l,r] [l,r] 内肯定至少有两个 d d d 的倍数。我们可以通过从 n n n 到 1 1 1 去枚举 g c d gcd gcd ,设其为 d d d ,然后找到所有 d d d 的倍数的下标。我们可以先不考虑其它 g c d gcd gcd 对此次的计算影响,只考虑计算 m a x _ g c d = d max\_gcd=d max_gcd=d 的最终答案。那么假设处理出来了其倍数下标排序后为 [ 1 , 3 , 5 , 10 ] [1,3,5,10] [1,3,5,10] ,那么我们怎么计算有多少个区间满足 v ( l , r ) = d v(l,r)=d v(l,r)=d 呢?我们考虑枚举左端点 l l l , l ∈ [ 1 , 1 ] l∈[1,1] l∈[1,1] , r r r 为 3 3 3 及以后皆可; l ∈ [ 2 , 3 ] l∈[2,3] l∈[2,3] , r r r 为 5 5 5 及其以后, l ∈ [ 4 , 5 ] l∈[4,5] l∈[4,5] , r r r 为 5 5 5 到尾。到这里应该找到规律了吧,也就是枚举每个下标 p [ i ] p[i] p[i] ,其合理左端点区间为 [ p [ i − 1 ] + 1 , p [ i ] ] [p[i-1]+1,p[i]] [p[i−1]+1,p[i]] ,对应的右端点为 [ p [ i + 1 ] , n ] [p[i+1],n] [p[i+1],n] 。 那么现在考虑其它 g c d gcd gcd 的影响,其实也就是更大的 g c d gcd gcd 对计算的影响。我们就想我们是否可以枚举每个左端点进行计算的时候,是否可以把更大的答案的影响消去。那么其实我们对于每个左端点 l l l 维护尚未确定 v v v 值的最大的右端点 m x [ l ] mx[l] mx[l] ,然后我们可以发现一个性质, m x [ l ] mx[l] mx[l] 是单调递增的,因为假设一个左端点 l 1 l_1 l1 的值为 m x [ l 1 ] mx[l_1] mx[l1] ,假设 l 2 > l 1 l_2>l_1 l2>l1 且 m x [ l 2 ] < m x [ l 1 ] mx[l_2]<mx[l_1] mx[l2]<mx[l1] ,也就是说 [ l 2 , m x [ l 2 ] + 1 ] [l_2,mx[l_2]+1] [l2,mx[l2]+1] 这个区间是已经确定了 v v v 值的,而这个区间一定是 [ l 1 , m x [ l 1 ] ] [l_1,mx[l_1]] [l1,mx[l1]] 的一个子区间,子区间都确定了 v v v 值,那父区间也必确定了 v v v 值,相互矛盾,这个情况不可能发生。所以我们只要用线段树维护 m x [ i ] mx[i] mx[i] 的区间最大值和区间和,我们在枚举 [ p [ i − 1 ] + 1 , p [ i ] ] [p[i-1]+1,p[i]] [p[i−1]+1,p[i]] 的时候,就可以通过在线段树上二分的方法找到一个需要更新的左端点的最小值 L L L ,然后就可以将 [ L , p [ i ] ] [L,p[i]] [L,p[i]] 区间覆盖为 p [ i + 1 ] − 1 p[i+1]-1 p[i+1]−1 ,然后不断枚举左端点继续往后更新。 更 新 前 的 根 节 点 区 间 和 更新前的根节点区间和 更新前的根节点区间和 与 所 有 更 新 都 完 成 后 的 根 节 点 的 区 间 和 所有更新都完成后的根节点的区间和 所有更新都完成后的根节点的区间和 的差即是这个点的答案。
#include <bits/stdc++.h>
#define lson rt<<1
#define rson (rt<<1)|1
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, a[N], rk[N];
ll sum[N << 2];
int lz[N << 2], mx[N << 2];
int pos[N], len;
ll ans[N];
void push_up(int rt) {
sum[rt] = sum[lson] + sum[rson];
mx[rt] = max(mx[lson], mx[rson]);
}
void push_down(int rt, int l, int r) {
if (!lz[rt]) return ;
int mid = l + r >> 1;
lz[lson] = lz[rson] = lz[rt];
mx[lson] = mx[rson] = lz[rt];
sum[lson] = 1ll * (mid - l + 1) * lz[rt];
sum[rson] = 1ll * (r - mid) * lz[rt];
lz[rt] = 0;
}
void build(int rt, int l, int r) {
lz[rt] = 0;
if (l == r) {
sum[rt] = n;
mx[rt] = n;
return ;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
push_up(rt);
}
void update(int rt, int l, int r, int L, int R, int v) {
if (L <= l && r <= R) {
sum[rt] = 1ll * (r - l + 1) * v;
mx[rt] = v;
lz[rt] = v;
return ;
}
push_down(rt, l, r);
int mid = l + r >> 1;
if (mid >= L) update(lson, l, mid, L, R, v);
if (mid < R) update(rson, mid + 1, r, L, R, v);
push_up(rt);
}
int fd(int rt, int l, int r, int L, int R, int v) {
if (l == r) {
if (l > R || mx[rt] <= v) return -1;
else if (l < L) return L;
else return l;
}
push_down(rt, l, r);
int mid = l + r >> 1;
if (mx[lson] > v) return fd(lson, l, mid, L, R, v);
else return fd(rson, mid + 1, r, L, R, v);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int T;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
rk[a[i]] = i;
}
build(1, 1, n);
for (int i = n; i >= 1; --i) {
len = 0;
for (int j = i; j <= n; j += i) {
pos[++len] = rk[j];
}
sort(pos + 1, pos + 1 + len);
ans[i] = sum[1];
for (int j = 1; j + 1 <= len; ++j) {
int l = fd(1, 1, n, pos[j - 1] + 1, pos[j], pos[j + 1] - 1);
if (~l) {
update(1, 1, n, l, pos[j], pos[j + 1] - 1);
}
}
ans[i] -= sum[1];
}
for (int i = 1; i <= n; ++i) {
printf("%lld\n", ans[i]);
}
}
}