杜教筛

杜教筛

1、非线性时间复杂度:比线性时间更快,杜教筛的时间复杂度差不多为 \(n^{\frac{2}{3}}\),杜教筛用于计算一类数论函数的前缀和,例如 \(S(n)=\sum_{i=1}^{n}f(i)\)

2、现在我们构造一个 \(S(n)\) 关于 \(S(\left \lfloor \frac{n}{i} \right \rfloor)\) 的递推式。

3、对于任意一个数论函数 \(g\),必满足:

\(\sum_{i=1}^{n}(f * g)(i)=\sum_{i=1}^{n}\sum_{d|i}g(d)f(\frac{i}{d})=\sum_{i=1}^{n}g(i)S(\left \lfloor \frac{n}{i} \right \rfloor)\)

4、杜教筛公式为:

\(g(1)S(n)=\sum_{i=1}^{n}g(i)S(\left \lfloor \frac{n}{i} \right \rfloor)-\sum_{i=2}^{n}g(i)S(\left \lfloor \frac{n}{i} \right \rfloor)=\sum_{i=1}^{n}(f*g)(i)-\sum_{i=2}^{n}g(i)S(\left \lfloor \frac{n}{i} \right \rfloor)\)

5、其中里面的 \(S(n)\) 代表的是 数论函数 \(f(x)\) 的前缀和,用此公式的前提是能求出前面那个的前缀和,后面那部分直接数论分块进行求解即可。

6、模版:洛谷P4213

\(\sum_{i=1}^{n}\sum_{j=1}^{n}i·j·gcd(i,j)\ (mod \ p) \qquad (n \le 1e10,5e8 \le p \le 1.1*1e9,p是质数)\)

struct sieve{
	int N;
	vector<i64> a;
	vector<i64> preim;
	vector<i64> Mu, phi;
	unordered_map<i64, i64> S_phi, S_Mu;
	
	sieve(int N_) : a(N_ + 1), Mu(N_ + 1), phi(N_ + 1) {
		N = N_;
		Mu[1] = phi[1] = 1;
		for (i64 i = 2; i <= N; i++) {
			if (!a[i]) {
				a[i] = i;// 该数的最小质因子
				Mu[i] = -1;// 该数的莫比乌斯函数值->与包含的质因子数目有关
				phi[i] = i - 1;// 该数的欧拉函数值->和他互质的数的数目
				preim.push_back(i);
			}
			for (auto p : preim) {
				if (i * p > N) break;
				a[i * p] = p;
				if (a[i] == p) {
					phi[i * p] = phi[i] * p;
					break;
				}
				Mu[i * p] = -Mu[i];
				phi[i * p] = phi[i] * phi[p];
			}
		}
		
		for (i64 i = 1; i <= N; i++) {// 记录前缀和
			Mu[i] += Mu[i - 1];
			phi[i] += phi[i - 1];
		}
	}
	
	i64 get_smu(i64 x) {// 得到前缀和长度大于 $1e7$ 的值
		if (x <= N) return Mu[x];
		if (S_Mu.count(x)) return S_Mu[x];
		i64 ans = 1;
		for (i64 l = 2, r; l <= x; l = r + 1) {
			r = x / (x / l);
			ans -= (r - l + 1) * get_smu(x / l);
		}
		return S_Mu[x] = ans;
	}
	
	i64 get_sphi(i64 x) {// 得到前缀和长度大于 $1e7$ 的值
		if (x <= N) return phi[x];
		if (S_phi.count(x)) return S_phi[x];
		i64 ans = x * (x + 1) / 2;
		for (i64 l = 2, r; l <= x; l = r + 1) {
			r = x / (x / l);
			ans -= (r - l + 1) * get_sphi(x / l);
		}
		return S_phi[x] = ans;
	}
};

sieve t(10000000);

void solve() {
	i64 n;
	cin >> n;
	printf("%lld %lld\n", t.get_sphi(n), t.get_smu(n));
}
posted @ 2024-08-29 23:30  grape_king  阅读(38)  评论(0)    收藏  举报