P5071 [Ynoi] 此时此刻的光辉
由乃不毒瘤的一集。(题目背景赞)
这题目要查询区间的约数个数,自然是先要把每个数先分解质因数再求解。但是我们发现一个 \(x \le 10^9\) 的 \(x\) 最多有 \(10\) 个不用的因数,如果再加个莫队的话时间复杂度变为 \(O(n\sqrt{m} \times 10)\) 大约是 \(3.1\times 10^8\),在 Ynoi 上必然超时。
于是我们考虑再根号分治一下。对 \(p\le 1000\) 的质因数,我们可以直接前缀和存下来,由于只有 \(168\) 个,于是不会超时。然后一个数最多只剩下两个 \(>1000\) 的质因数,用 Pollard Rho 就跑的飞快了。这个时候再去莫队维护就跑的没问题了 不用卡常就是跑的飞快。
然后研究一下莫队怎么写。把所有的大质数都离散化一下,然后根据当 \(a=p_1^{c_1}\times p_2^{c_2} \times \ldots \times p_n^{c_n}\) 时它的约数个数 = \((c_1+1)\times (c_2+1)\times \ldots \times (c_n+1)\)。当一种质数个数 \(+1\) 时, \(ans\) 就乘上 \(\frac{c+2}{c+1}\),\(-1\) 同理,简单计数即可。
Code:
#include <bits/stdc++.h>
#define ll long long
#define i128 __int128
using namespace std;
const int N = 1e5 + 10, M = 210, mod = 19260817;
int pri[M], tot, pos[1010], inv[N * 3];
int n, m;
int sum[N][M];
ll b[N << 1], b_cnt, p[N][4], pcnt[N], cnt[N << 1], Ans[N];
ll ans = 1;
int B;
struct Query{int l, r, id;} q[N];
bool cmp(Query a, Query b) {
return a.l / B != b.l / B ? a.l < b.l : ((a.l / B) & 1) ? a.r < b.r : a.r > b.r;
}
ll gcd(ll a, ll b) {return b == 0 ? a : gcd(b, a % b);}
ll qpow(ll a, ll b, ll mod) {
ll ans = 1;
while(b) {
if(b & 1) ans = (i128)ans * a % mod;
a = (i128)a * a % mod; b >>= 1;
}
return ans;
}
bool Miller_Rabin(ll p) {
if(p < 2) return 0;
if(p <= 3) return 1;
ll d = p - 1, r = 0;
while(!(d & 1)) r++, d >>= 1;
for(ll k = 0; k <= 9; k++) {
ll a = rand() % (p - 2) + 2;
ll x = qpow(a, d, p);
if(x == 1 || x == p - 1) continue;
for(int i = 0; i < r - 1; i++) {
x = (i128)x * x % p;
if(x == p - 1) break;
}
if(x != p - 1) return 0;
}
return 1;
}
ll Pollard_Rho(ll x) {
ll a = 0, b = 0;
ll c = (ll)rand() % (x - 1) + 1;
ll val = 1;
for(int bas = 1; ; bas <<= 1, a = b, val = 1) {
for(int i = 1; i <= bas; i++) {
b = ((i128)b * b + c) % x;
val = (i128)val * abs(a - b) % x;
if(val == 0) return x;
if(i % 127 == 0) {
ll d = gcd(val, x);
if(d > 1) return d;
}
}
ll d = gcd(val, x);
if(d > 1) return d;
}
return x;
}
void get_fac(ll x, int id) {
if(x <= 1000) return;
if(Miller_Rabin(x)) {
b[++b_cnt] = x; p[id][++pcnt[id]] = x;
return;
}
ll p = x;
while(p == x) p = Pollard_Rho(x);
get_fac(p, id); get_fac(x / p, id);
}
void init() {
for(int i = 2; i <= 1000; i++) if(Miller_Rabin(i)) pri[++tot] = i, pos[i] = tot;
inv[1] = 1;
for(int i = 2; i < N * 3; i++) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
}
void add(int id) {
for(int j = 1; j <= pcnt[id]; j++) {
ans = ans * inv[cnt[p[id][j]] + 1] % mod * (cnt[p[id][j]] + 2) % mod;
cnt[p[id][j]]++;
}
}
void del(int id) {
for(int j = 1; j <= pcnt[id]; j++) {
ans = ans * inv[cnt[p[id][j]] + 1] % mod * cnt[p[id][j]] % mod;
cnt[p[id][j]]--;
}
}
int main() {
init();
scanf("%d%d", &n, &m); B = max<int>(1, n / sqrt(m));
for(int i = 1; i <= n; i++) {
int x; scanf("%d", &x);
for(int j = 1; j <= tot; j++) sum[i][j] = sum[i - 1][j];
for(int j = 1; j <= tot && pri[j] * pri[j] <= x; j++) {
while(x % pri[j] == 0) x /= pri[j], sum[i][j]++;
}
if(x > 1) {
if(x <= 1000) sum[i][pos[x]]++;
else get_fac(x, i);
}
}
sort(b + 1, b + 1 + b_cnt); b_cnt = unique(b + 1, b + 1 + b_cnt) - b - 1;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= pcnt[i]; j++) {
p[i][j] = lower_bound(b + 1, b + 1 + b_cnt, p[i][j]) - b;
}
}
for(int i = 1; i <= m; i++) scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i;
sort(q + 1, q + 1 + m, cmp);
int l = 1, r = 0;
for(int i = 1; i <= m; i++) {
while(l > q[i].l) add(--l);
while(r < q[i].r) add(++r);
while(l < q[i].l) del(l++);
while(r > q[i].r) del(r--);
ll res = ans;
for(int j = 1; j <= tot; j++) {
res = res * (sum[r][j] - sum[l - 1][j] + 1) % mod;
}
Ans[q[i].id] = res;
}
for(int i = 1; i <= m; i++) printf("%lld\n", Ans[i]);
return 0;
}

浙公网安备 33010602011771号