题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=7107
经典统计所有 ((l,r)) 区间的问题,所以固定右端点进行考虑
观察到如果固定右端点 (r),(l) 到 (r) 区间内的 (gcd) 最大值随着 (l) 从右到左而逐渐变大
同时,如果区间 ([i,j]) 内的最大公因数为 (x),则 ([i,j]) 内一定至少存在两个 (x) 的倍数
于是我们考虑从大到小枚举 (gcd),同时维护每个右端点当前未确定答案的最左边的位置(([l,r]) 中 (gcd) 越大的 (l) 越靠左),于是相当于每次遍历 (gcd) 倍数所在的位置 (g[i]),将 ([g[i+1],n]) 内的答案与 (g[i]+1) 取最大值(当前已确定固定的 (r) 的左端 (l) 为 ([1,g[i]])),可以使用 (Segment tree beats) 解决
又可以注意到,对于将要进行区间取最大值操作的 ([g[i+1],n]) 这段区间,左端点固定,则 (r) 越大,区间中最大 (gcd) 越大,所以 ([g[i+1],n]) 区间内维护的最左端的位置也是单调递增的,所以只需要在线段树内二分出这段区间中最后一个需要更新的位置,使用区间覆盖更新即可,每个 (gcd) 操作前后线段树维护的最左端位置的和的差(每个固定右端点更新的区间长度和)即为该 (gcd) 的答案
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int T, n;
int a[maxn], p[maxn];
ll ans[maxn];
vector<int> fac[maxn];
struct Node{
int tag, mi;
ll sum;
}t[maxn<<2];
void pushup(int i){
t[i].mi = min(t[i<<1].mi, t[i<<1|1].mi);
t[i].sum = t[i<<1].sum + t[i<<1|1].sum;
}
void build(int i, int l, int r){
if(l == r){
t[i].tag = 0;
t[i].mi = 1;
t[i].sum = 1ll;
return;
}
int mid = (l+r) >> 1;
build(i<<1, l, mid); build(i<<1|1, mid+1, r);
pushup(i);
}
void pushdown(int i, int l, int r){
if(t[i].tag){
t[i<<1].tag = t[i<<1|1].tag = t[i].tag;
int mid = (l+r) >> 1;
t[i<<1].mi = t[i].tag;
t[i<<1|1].mi = t[i].tag;
t[i<<1].sum = 1ll * t[i].tag * (mid-l+1);
t[i<<1|1].sum = 1ll * t[i].tag * (r-mid);
t[i].tag = 0;
}
}
void modify(int i, int k, int l, int r, int x, int y){
if(x <= l && r <= y){
t[i].tag = k;
t[i].mi = k;
t[i].sum = 1ll * k * (r-l+1);
return;
}
pushdown(i, l, r);
int mid = (l+r) >> 1;
if(x <= mid) modify(i<<1, k, l, mid, x, y);
if(y > mid) modify(i<<1|1, k, mid + 1, r, x, y);
pushup(i);
}
int find(int i, int k, int l, int r, int x){
if(l == r){
return l;
}
pushdown(i, l, r);
int mid = (l+r) >> 1;
int pos = -1;
if(t[i<<1|1].mi < k) pos = find(i<<1|1, k, mid+1, r, x);
if(pos != -1) return pos;
if(x <= mid && t[i<<1].mi < k) pos = find(i<<1, k, l, mid, x);
return pos;
}
ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
int main(){
T = read();
while(T--){
memset(t, 0, sizeof(t));
memset(ans, 0, sizeof(ans));
n = read();
for(int i = 1 ; i <= n ; ++i) {
a[i] = read(), p[a[i]] = i;
}
for(int i = 1 ; i <= n ; ++i){
fac[i].clear();
for(int j = i ; j <= n ; j += i){
fac[i].push_back(p[j]);
}
sort(fac[i].begin(), fac[i].end());
}
build(1, 1, n);
for(int g = n ; g >= 1 ; --g){
ll sum = t[1].sum;
for(int i = 0 ; i < fac[g].size()-1 ; ++i){
int pos = find(1, fac[g][i]+1, 1, n, fac[g][i+1]);
if(pos != -1 && pos >= fac[g][i+1]) modify(1, fac[g][i]+1, 1, n, fac[g][i+1], pos);
}
ans[g] = t[1].sum - sum;
}
for(int g = 1 ; g <= n ; ++g){
printf("%lld
", ans[g]);
}
}
return 0;
}