给你一个序列 a a a,每次询问给一个区间 [ l , r ] [l,r] [l,r]。
查询 l ≤ i , j ≤ r l \leq i,j \leq r l≤i,j≤r 且 a i a_i ai 是 a j a_j aj 倍数的二元组 ( i , j ) (i,j) (i,j) 的个数。
1 ≤ n , m , a i ≤ 5 × 1 0 5 1 \leq n,m,a_i\leq 5\times 10^5 1≤n,m,ai≤5×105,时限 3 s 3\text{s} 3s,空限 128 MB 128\text{MB} 128MB。
sol
「点缀光辉的第十四分块」。
考虑二次离线莫队。
关于二次离线莫队,不懂点 这。
设 a i a_i ai 在区间 [ l , r ] [l,r] [l,r] 的因数个数为 f 1 ( i , [ l , r ] ) f_1(i,[l,r]) f1(i,[l,r]),倍数个数为 f 2 ( i , [ l , r ] ) f_2(i,[l,r]) f2(i,[l,r])。
则右端点向右移动加入一个数的贡献为 f 1 ( r , [ l , r ] ) + f 2 ( r , [ l , r ] ) f_1(r,[l,r])+f_2(r,[l,r]) f1(r,[l,r])+f2(r,[l,r])。
差分一下即为 f 1 ( r , [ 1 , r ] ) − f 1 ( r , [ 1 , l − 1 ] ) + f 2 ( r , [ 1 , r ] ) − f 2 ( r , [ 1 , l − 1 ] ) f_1(r,[1,r])-f_1(r,[1,l-1])+f_2(r,[1,r])-f_2(r,[1,l-1]) f1(r,[1,r])−f1(r,[1,l−1])+f2(r,[1,r])−f2(r,[1,l−1])。
f 1 ( r , [ 1 , r ] ) f_1(r,[1,r]) f1(r,[1,r]) 可以在遍历时 O ( n ) \mathcal O(\sqrt n) O(n) 枚举因数,累加所有因数之前的出现次数,定义一个桶即可实现。
f 2 ( r , [ 1 , r ] ) f_2(r,[1,r]) f2(r,[1,r]) 可以在上一个操作枚举因数时对该数的每个因数标记一下,即开一个桶记录该数作为因数的出现次数。
以上两部分可提前预处理,计算前缀和,然后跑一边莫队计算贡献,时间复杂度为 O ( n n ) \mathcal O(n\sqrt n) O(nn)。
对于 f 1 ( r , [ 1 , l − 1 ] ) f_1(r,[1,l-1]) f1(r,[1,l−1]) 和 f 2 r , [ 1 , l − 1 ] f_2{r,[1,l-1]} f2r,[1,l−1] 可在上面跑莫队时顺便存下来,进行二次离线,要求 O ( 1 ) \mathcal O(1) O(1) 查询 f 1 f_1 f1 和 f 2 f_2 f2。
考虑二次离线时的修改,这部分要求是 O ( n ) \mathcal O(\sqrt n) O(n) 级别的。
对于 f 1 f_1 f1,枚举因数,加上贡献即可。
对于 f 2 f_2 f2,考虑根号分治,设阈值 S S S:
- 若当前加入的数 ≥ S \geq S ≥S,暴力跳表,加上贡献。
- 若当前加入的数 < S < S <S,则将这个提出二次离线这部分,后面处理。
二次离线部分时间复杂度为 O ( n n ) \mathcal O(n \sqrt n) O(nn)。
再考虑刚刚遗留下来的一点东西。
记 p [ i ] [ j ] p[i][j] p[i][j] 表示区间 [ 1 , j ] [1,j] [1,j] 中含有因数 i i i 的数的个数, c [ i ] [ j ] c[i][j] c[i][j] 表示区间 [ 1 , j ] [1,j] [1,j] 中 i i i 的出现次数。
则区间 [ l , r ] [l,r] [l,r] 中含有因数 i i i 的数的个数就是 p [ i ] [ r ] − p [ i ] [ l − 1 ] p[i][r] - p[i][l-1] p[i][r]−p[i][l−1]。
所以 [ 1 , z ] [1,z] [1,z] 中的数 i i i 作为因数出现在 [ l , r ] [l,r] [l,r] 的总次数为 c [ i ] [ z ] × ( p [ i ] [ r ] − p [ i ] [ l − 1 ] ) c[i][z]\times (p[i][r] - p[i][l-1]) c[i][z]×(p[i][r]−p[i][l−1])。
那么枚举 i ∈ [ 1 , S ) i \in [1,S) i∈[1,S), O ( n ) \mathcal O(n) O(n) 求出 p p p 和 c c c(压掉一维),然后 O ( m ) \mathcal O(m) O(m) 计算贡献即可。
所以总时间复杂度为 O ( n n ) \mathcal O(n \sqrt n) O(nn),总空间复杂度为 O ( n ) \mathcal O(n) O(n)。
32.39s / 75.05MB / 3.92KB C++11 O2 \text{32.39s / 75.05MB / 3.92KB C++11 O2} 32.39s / 75.05MB / 3.92KB C++11 O2。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
namespace Fread
{
const int SIZE = 1 << 23;
char buf[SIZE], *S, *T;
inline char getchar()
{
if (S == T)
{
T = (S = buf) + fread(buf, 1, SIZE, stdin);
if (S == T)
return '\n';
}
return *S++;
}
}
namespace Fwrite
{
const int SIZE = 1 << 23;
char buf[SIZE], *S = buf, *T = buf + SIZE;
inline void flush()
{
fwrite(buf, 1, S - buf, stdout);
S = buf;
}
inline void putchar(char c)
{
*S++ = c;
if (S == T)
flush();
}
struct NTR
{
~NTR()
{
flush();
}
} ztr;
}
#ifdef ONLINE_JUDGE
#define getchar Fread::getchar
#define putchar Fwrite::putchar
#endif
typedef long long ll;
inline int read()
{
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9')
{
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
inline void write(ll x)
{
if(x < 0)
{
putchar('-');
x = -x;
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
const int _ = 5e5 + 7, S = 100;
int n, m, k, a[_], bel[_], siz, mx, cnt[_], flg[_], pre[_];
ll f[_], Ans[_];
struct Query
{
int l, r, id;
ll ans;
inline bool operator < (const Query &t) const
{
return bel[l] == bel[t.l] ? (bel[l] & 1 ? r < t.r : r > t.r) : bel[l] < bel[t.l];
}
} q[_];
struct Node
{
int l, r, id, x;
bool operator < (const Node &t) const
{
return x < t.x;
}
} t[_ << 1];
std::vector<int> fac[_];
signed main()
{
n = read(), m = read();
siz = sqrt(n);
for(int i = 1; i <= n; ++i)
{
a[i] = read();
bel[i] = (i - 1) / siz + 1;
mx = std::max(mx, a[i]);
if(fac[a[i]].empty())
{
for(int j = 1; j * j <= a[i]; ++j)
if(a[i] % j == 0)
{
f[i] += cnt[j];
flg[j]++;
if(j * j != a[i])
{
f[i] += cnt[a[i] / j];
flg[a[i] / j]++;
}
fac[a[i]].emplace_back(j);
}
}
else
{
for(int x : fac[a[i]])
{
f[i] += cnt[x];
flg[x]++;
if(x * x != a[i])
{
f[i] += cnt[a[i] / x];
flg[a[i] / x]++;
}
}
}
f[i] += f[i - 1] + flg[a[i]];
cnt[a[i]]++;
}
for(int i = 1; i <= m; ++i)
q[i] = {read(), read(), i, 0};
std::sort(q + 1, q + m + 1);
for(int i = 1, l = 1, r = 0; i <= m; ++i)
{
if(r < q[i].r) t[++k] = {r + 1, q[i].r, -i, l - 1}, q[i].ans += f[q[i].r] - f[r], r = q[i].r;
if(l > q[i].l) t[++k] = {q[i].l, l - 1, i, r}, q[i].ans -= f[l - 1] - f[q[i].l - 1], l = q[i].l;
if(r > q[i].r) t[++k] = {q[i].r + 1, r, i, l - 1}, q[i].ans -= f[r] - f[q[i].r], r = q[i].r;
if(l < q[i].l) t[++k] = {l, q[i].l - 1, -i, r}, q[i].ans += f[q[i].l - 1] - f[l - 1], l = q[i].l;
}
std::sort(t + 1, t + k + 1);
memset(f, 0, sizeof f);
for(int i = 1, x = 0; i <= k; ++i)
{
while(x < t[i].x)
{
x++;
for(int v : fac[a[x]])
{
f[v]++;
if(v * v != a[x])
f[a[x] / v]++;
}
if(a[x] > S)
for(int j = 1; j * a[x] <= mx; ++j)
f[j * a[x]]++;
}
for(int k = t[i].l; k <= t[i].r; ++k)
if(t[i].id < 0)
q[-t[i].id].ans -= f[a[k]];
else
q[t[i].id].ans += f[a[k]];
}
for(int i = 1; i <= S; ++i)
{
cnt[0] = pre[0] = 0;
for(int j = 1; j <= n; ++j)
{
cnt[j] = cnt[j - 1] + (a[j] == i);
pre[j] = pre[j - 1] + (a[j] % i == 0);
}
for(int j = 1; j <= k; ++j)
if(t[j].id < 0)
q[-t[j].id].ans -= (ll)cnt[t[j].x] * (ll)(pre[t[j].r] - pre[t[j].l - 1]);
else
q[t[j].id].ans += (ll)cnt[t[j].x] * (ll)(pre[t[j].r] - pre[t[j].l - 1]);
}
for(int i = 1; i <= m; ++i)
{
q[i].ans += q[i - 1].ans;
Ans[q[i].id] = q[i].ans;
}
for(int i = 1; i <= m; ++i)
write(Ans[i]), putchar('\n');
}