[Ynoi2015] 此时此刻的光辉

在太阳西斜的这个世界里,置身天上之森。等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去、逐渐消逝的未来。我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘。————世界上最幸福的女孩 珂朵莉

我永远喜欢珂朵莉。


Description

给定长度为 \(n\) 的数列 \(a\) ,和 \(m\) 个询问 \(l, r\)

求每个区间内所有数相乘的因数个数。

\(1\leq n,\ m\leq 10 ^ 5\)\(1\leq a\leq 10 ^ 9\)

Analysis

先想想对于任意一个数 \(x\) ,约数个数是多少个。

首先先分解,大概就先表示成:

\[x = \prod p_i ^ {a_i} \]

那么约数个数就是每种质数的选择数量是 \(0 - a_i\) ,所以式子写出来就是:

\[num = \prod (a_i + 1) \]

于是乎有个很显然的想法,上莫队维护区间内每个质数的数量。

一看值域,一共会有 50847534 个质数,这时间不直接爆炸。。

Solution

话是这么说,但是肯定总体的解题方式大差不差了。

考虑怎么去优化,可以先想到根号分治,对于小于 \(\sqrt{a}\) 的质因子预处理,大于的再在莫队里面维护。

这么看的话,对于一个值域 \(a\)\(10 ^ 9\) 的数, \(\sqrt{a}\) 内的质数有 3400 左右个。即便与处理完所有数,每个数只会剩下不超过 1 个质因子,但显然体量上不能支持我们筛这么多质数。

但还是继续借助根号分治的思路,平衡下质数数量,我们可以考虑把 \(\sqrt{a}\) 换成 \(\sqrt[3]{a}\) 。这样的话要预处理的质数变成了 168 个,比较能接受,同时预处理后的数也顶多会有 2 个质因子。

现在就是要快速把预处理后的数分解质因子,普通分解不太行,考虑 PR 算法。因为只有 2 个质因子,所以做一次 PR ,然后把找出来的质因子离散化一下就行了。

(PS: PR 算法的话不太确定 \(\log ^ 2\) 的写法能不能过,还是推荐写单 \(\log\) 的写法)

那么这样的话,我们就把原来的 \(O(过不了)\) 的纯莫队分成了一个 \(O(168n)\) 的预处理加计算和一个 \(O(n\sqrt{n})\) 的莫队,也就能完成这道题了。

Code

/*

*/
#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int mod = 19260817, N = 2e5 + 10;
const int pri[] = {2, 3, 5, 7, 11, 13, 17};
int now;
int n, m, boc, col[N], blo[N], lt[N], rt[N], id[N];
int num[171][N], yz[N], tim, cnt[N], res = 1, ret, ans[N];
int pr[171], tot, vis[1008], inv[N];
std::unordered_map<int, int> mp;
inline int read() {
	char ch = getchar();
	int s = 0, w = 1;
	while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {s = (s << 3) + (s << 1) + ch - '0'; ch = getchar();}
	return s * w;
}
inline int mul(int a, int b, int p) {
	return 1ll * a * b % p;
}
inline int gcd(int x, int y) {
	if (!x) return y;
	if (!y) return x;
	int t = __builtin_ctzll(x | y);
	x >>= __builtin_ctzll(x);
	while (y) {
		y >>= __builtin_ctzll(y);
		if (x > y) swap(x, y);
		y -= x;
	}
	return x << t;
}
inline int ksm(int a, int b, int p) {
	int tmp = 1;
	while (b) {
		if (b & 1) tmp = mul(tmp, a, p);
		a = mul(a, a, p); b >>= 1;
	}
	return tmp;
}
inline bool MR(int a, int p) {
	int tmp, B = p - 1, num = 0;
	while ((B ^ 1) & 1) B >>= 1, ++num;
	tmp = ksm(a, B, p);
	if (!(tmp ^ 1)) return 0;
	while (num--) {
		if (tmp ^ 1 && tmp ^ (p - 1)) {
			tmp = mul(tmp, tmp, p);
			if (!(tmp ^ 1)) return 1;
		}
		else tmp = mul(tmp, tmp, p);
	}
	return (tmp ^ 1);
}
inline bool isprime(int p) {
	if (p == 2) return 1;
	if (p < 2 || !(p & 1)) return 0;
	for (ri i = 0; i < 7; ++i) {
		if (!(pri[i] ^ p)) return 1;
		if (MR(pri[i], p)) return 0;
	}
	return 1;
}
inline int F(int a, int c, int p) {
	return (mul(a, a, p) + c) % p;
}
int t, a, b, c, d;
inline int PR(int p) {
	if (!(p & 1)) return 2;
	if (isprime(p)) return p;
	a = b = 0; t = 1;
	c = rand() % (p + 1);
	for (ri dep = 1; ; dep <<= 1) {
		a = b; t = 1;
		for (ri i = 1; i <= dep; ++i) {
			b = F(b, c, p);
			t = mul(t, abs(a - b), p);
			if (!(i & 127)) {
				d = gcd(t, p);
				if (d > 1) return d;
			}
		}
		d = gcd(t, p);
		if (d > 1) return d;
	}
}
inline void mxprime(int p) {
	if (p <= now) return ;
	if (isprime(p)) {
		now = max(now, p);
		return ;
	}
	int q = PR(p);
	while (q >= p) q = PR(p);
	mxprime(q); mxprime(p / q);
}
inline void init() {
	vis[1] = 1;
	for (ri i = 2; i <= 1000; ++i) {
		if (!vis[i]) pr[++tot] = i;
		for (ri j = 1; j <= tot && pr[j] * i <= 1000; ++j) {
			vis[i * pr[j]] = 1;
			if (i % pr[j] == 0) break;
		}
	}
	inv[1] = 1;
	for (ri i = 2; i <= (n << 1); ++i) {
		inv[i] = mul(mod - mod / i, inv[mod % i], mod);
	}
}
inline bool cmp(int x, int y) {
	return (blo[x] ^ blo[y]) ? (lt[x] < lt[y]) : (rt[x] < rt[y]);
}
inline void add(int x) {
	if (col[x]) {
		++cnt[col[x]];
		res = mul(res, mul(inv[cnt[col[x]] - 1], cnt[col[x]], mod), mod);
		if (yz[x]) {
			++cnt[yz[x]];
			res = mul(res, mul(inv[cnt[yz[x]] - 1], cnt[yz[x]], mod), mod);
		}
	}
}
inline void del(int x) {
	if (col[x]) {
		--cnt[col[x]];
		res = mul(res, mul(inv[cnt[col[x]] + 1], cnt[col[x]], mod), mod);
		if (yz[x]) {
			--cnt[yz[x]];
			res = mul(res, mul(inv[cnt[yz[x]] + 1], cnt[yz[x]], mod), mod);
		}
	}
}
int main() {
	srand(19260817);
	n = read(); m = read(); boc = sqrt(n);
	init();
	for (ri i = 1; i <= n; ++i) {
		col[i] = read();
		for (ri j = 1; j <= tot; ++j) {
			while (!(col[i] % pr[j])) {
				col[i] /= pr[j]; ++num[j][i];
			}
			num[j][i] += num[j][i - 1];
		}
		if (col[i] > 1 && !isprime(col[i])) {
			now = 1; mxprime(col[i]);
			yz[i] = now; col[i] /= yz[i];
		}
		if (col[i] > 1) {
			if (yz[i]) {
				if (!mp.count(yz[i])) mp[yz[i]] = ++tim;
				yz[i] = mp[yz[i]];
			}
			if (!mp.count(col[i])) mp[col[i]] = ++tim;
			col[i] = mp[col[i]];
		}
		else col[i] = 0;
	}
	for (ri i = 1; i <= m; ++i) {
		lt[i] = read(); rt[i] = read();
		blo[i] = lt[i] / boc; id[i] = i;
	}
	sort(id + 1, id + 1 + m, cmp);
	for (ri i = 1; i <= tim; ++i) cnt[i] = 1;
	int l = 1, r = 0;
	for (ri i = 1; i <= m; ++i) {
		int ql = lt[id[i]], qr = rt[id[i]];
		while (l > ql) add(--l);
		while (r < qr) add(++r);
		while (l < ql) del(l++);
		while (r > qr) del(r--);
		ret = res;
		for (ri j = 1; j <= tot; ++j) {
			ret = mul(ret, num[j][r] - num[j][l - 1] + 1, mod);
		}
		ans[id[i]] = ret;
	}
	for (ri i = 1; i <= m; ++i) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2021-12-16 16:47  Illusory_dimes  阅读(52)  评论(0编辑  收藏  举报