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;
}
posted @ 2026-07-25 14:57  OIerYang  阅读(7)  评论(0)    收藏  举报