題目鏈接: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\n", ans[g]);
}
}
return 0;
}
