区间素数筛法

有时候会求在大区间[L,R]内的素数个数,但是一般R-L不会很大,因此可以求[1,√R]中的素数来筛[L,R]中的素数。用求出的素数来筛[L,R]中的合数。因为L,R过大,开出的数组大小一般为最大的R-L。

例题①:https://vjudge.net/problem/POJ-2689
注意:1不为素数的情况。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxl = 5e4;
bool vis[maxl];
int p[maxl], tot;
int ans[1000005];
ll prime[1000005];
void init() {
	vis[0] = vis[1] = true;
	for (int i = 2; i < maxl; i++) {
		if (!vis[i])p[tot++] = i;
		for (int j = 0; j < tot && i * p[j] < maxl; j++) {
			vis[i * p[j]] = true;
			if (i % p[j] == 0)break;
		}
	}
}
int main(void) {
	init();
	ll L, R;
	while (~scanf("%lld %lld", &L, &R)) {
		memset(ans, 0, sizeof(ans));
		for (int i = 0; i < tot; i++) {
			for (ll j = max(2LL, (L - 1) / p[i] + 1) * p[i]; j <= R; j += p[i])
				if (j - L >= 0)ans[j - L] = 1;
		}
		if (L == 1)ans[0] = 1;
		int minn = inf, maxx = -1, cnt = 0;
		for (int i = 0; i <= R - L; i++)
			if (!ans[i])prime[cnt++] = i;
		int out[4] = { -1,-1,-1,-1 };
		for (int i = 1; i < cnt; i++) {
			if (minn > prime[i] - prime[i - 1]) {
				minn = prime[i] - prime[i - 1];
				out[0] = prime[i - 1]; out[1] = prime[i];
			}
			if (maxx < prime[i] - prime[i - 1]) {
				maxx = prime[i] - prime[i - 1];
				out[2] = prime[i - 1]; out[3] = prime[i];
			}
		}
		if (minn == inf || out[0] == -1) { printf("There are no adjacent primes.\n"); continue; }
		printf("%lld,%lld are closest, %lld,%lld are most distant.\n", L + out[0], L + out[1], L + out[2], L + out[3]);
	}
	return 0;
}

例题②:http://acm.hdu.edu.cn/showproblem.php?pid=6069
题意:求区间[L,R]中每个元素的K次方的约数个数之和,模998244353。
因为是大区间,预先求出1-1e6的素数,利用约数个数定理。两层循环,外层遍历每个素数,内层遍历在该区间内的该素数的倍数,不断分解质因子。注意若分解后的结果不为1,说明剩下的是一个大于1e6的素数。

#include<cstdio>
using namespace std;
typedef long long ll;
const int maxl = 1e6 + 1;
const ll mod = 998244353;
int p[maxl], tot, num[maxl];
bool vis[maxl];
ll val[maxl];
void init() {
	vis[0] = vis[1] = true;
	for (int i = 2; i < maxl; i++) {
		if (!vis[i])p[tot++] = i;
		for (int j = 0; j < tot && i * p[j] < maxl; j++) {
			vis[i * p[j]] = true;
			if (i % p[j] == 0)break;
		}
	}
}
ll qpow(ll a, ll b) {
	ll res = 1;
	while (b){
		if (b & 1)res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}
int main(void) {
	init();
	int T;
	for (scanf("%d", &T); T--;) {
		ll L, R, K;
		scanf("%lld %lld %lld", &L, &R, &K);
		for (int i = 0; i <= R - L; i++)num[i] = 1, val[i] = L + i;
		for (int i = 0; i < tot; i++) {
			//遍历在区间[L,R]内所有p[i]的倍数
			ll LL = ((L - 1) / p[i] + 1) * p[i];
			for (ll j = LL; j <= R; j += p[i]) {
				ll cnt = 0;
				while (val[j - L] % p[i] == 0)val[j - L] /= p[i], cnt++;
				num[j - L] = num[j - L] * (cnt * K % mod + 1) % mod;
			}
		}
		for (int i = 0; i <= R - L; i++)if (val[i] != 1)num[i] = num[i] * (K + 1) % mod;
		ll ans = 0;
		for (int i = 0; i <= R - L; i++)ans = (ans + num[i]) % mod;
		printf("%lld\n", ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JILIN.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值