📅  最后修改于: 2023-12-03 14:55:37.738000             🧑  作者: Mango
这是一种基于莫队算法的解法,用于查询给定范围内数组乘积的除数计数。这个问题可以转化为求每个质因子的个数之和,而这个问题可以用线性筛素数 + 莫队算法解决。
以下是使用 C++ 实现的代码:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 100010, maxd = 12, maxq = 100010;
int n, q, d; ll ans[maxq], sm[maxn];
int a[maxn], cnt[maxd][maxn], pri[maxn], miu[maxn];
vector<int> divs[maxn];
struct Query {
int l, r, id;
bool operator < (const Query& b) {
return l/d==b.l/d ? r<b.r : l<b.l;
}
} Q[maxq];
void init() {
pri[0] = pri[1] = miu[1] = 1;
for (int i=2; i<=n; ++i) {
if (!pri[i]) pri[++pri[0]] = i, miu[i] = -1;
for (int j=1; j<=pri[0] && i*pri[j]<=n; ++j) {
pri[i*pri[j]] = 1;
if (i%pri[j]==0) { miu[i*pri[j]] = 0; break; }
else miu[i*pri[j]] = -miu[i];
}
}
for (int i=1; i<=n; ++i) {
for (int j=i; j<=n; j+=i)
divs[j].push_back(i);
}
for (int i=1; i<=n; ++i) {
for (int d: divs[a[i]])
++cnt[d][i];
}
}
void upd(int l, int r, int val) { // add (+1)/substract (-1) 1 from cnt[d] with indices in [l, r]
for (int d: divs[val])
for (int i=lower_bound(cnt[d]+l, cnt[d]+r+1, 1)-cnt[d];
i<=r-l; i=lower_bound(cnt[d]+i+1, cnt[d]+r+1, 1)-cnt[d])
--cnt[d][l+i], ++cnt[d][l+i+1];
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> q, d = pow(n, 0.6666666);
for (int i=1; i<=n; ++i) cin >> a[i];
init();
for (int i=1; i<=q; ++i) cin >> Q[i].l >> Q[i].r, Q[i].id = i;
sort(Q+1, Q+1+q);
int l=1, r=0;
for (int i=1; i<=q; ++i) {
int L=Q[i].l, R=Q[i].r;
while (l>L) upd(--l, r, a[l]), ++sm[a[l]];
while (r<R) upd(l, ++r, a[r]), ++sm[a[r]];
while (l<L) --sm[a[l]], upd(l++, r, a[l]);
while (r>R) --sm[a[r]], upd(l, r--, a[r]);
ll res=0;
for (int j=1; j<maxd; ++j)
res += (miu[j]?sm[j]:0)*cnt[j][L-1];
ans[Q[i].id] = res;
}
for (int i=1; i<=q; ++i)
cout << ans[i] << "\n";
return 0;
}
n
, q
, d
表示输入的数列长度 $n$、询问数量 $q$、分块块长 $d$。a[]
数组表示输入的数列。cnt[][]
数组记录了区间中每个数在每个质因子下的质因子个数,即 $cnt_{i,j}$ 表示在区间 $[1,j]$ 内数 $i$ 有几个某个质因子。pri[]
表示数 $i$ 是否为质数。miu[]
表示 $i$ 的莫比乌斯函数值,由线性筛筛出。divs[][]
是一个二维容器,$divs_i$ 存放了数 $i$ 的所有因子(包括 $1$ 和自己)的集合。Query
是一个结构体,记录了区间左端点 l
、区间右端点 r
和查询编号 id
,并重载了用于莫队算法的小于运算符。init()
函数中先把质因子个数和莫比乌斯函数值线性筛出来,再预处理出每个数的所有因子。upd(l, r, val)
函数中,先枚举每个因子 d
,对于在左右端点区间内的数,限制其按顺序转移,每次把左端点区间加上 $1$,右端点区间缩减 $1$(如果有 $-1$ 的话),可以快速维护区间内每个数的各个质因子的个数。a[]
。调用 init()
初始化,读入 $q$ 个查询(使用莫队算法查询每个区间内的质因子个数和),并按照左端点所在块排序。循环中,维护区间 $[l,r]$,不断调用 upd
更新区间,直到涵盖区间 $[L,R]$,之后依次重复这个过程。计算完每个区间的答案后输出即可。这个算法的时间复杂度为 $O(n^{\frac{2}{3}} q \sqrt{n} + n \log n)$,其中前一项为莫队算法的复杂度,后一项主要用于初始化相关的数组。由于这个算法需要预处理 $n$ 个数的各个质因子个数,加上线性递推求莫比乌斯函数,所以时间常数比较大,以至于无法通过本场提高组 4 的数据。