积性函数的筛法

Python RK3588 MPP 8路视频硬解码!全体目光向我看齐! 本文重写了官方demo、加入了Zlmediakit进行拉流并通过pybind11将代码封装成so库,现在,在python中直接通过import的方式,就能完成基础的拉流解码操作,目前支持8路拉流解码!!! 阅读详情

下面的所有知识均属数论范畴。


欧拉筛

可以做到 O ( n ) O(n) O(n) 的线性复杂度!

可以筛所有的积性函数(即对于所有的 a ⊥ b a \perp b a⊥b 都满足 f ( a b ) = f ( a ) ∗ f ( b ) f(ab) = f(a)*f(b) f(ab)=f(a)∗f(b) 的函数)。

举两个例子:

线性筛欧拉函数:

inline void Sieve()
{
    phi[1] = 1;
    for(register int i = 2; i <= MAXN; i++)	//MAXN表示值域
    {
        if(!NotPrime[i])	//是质数
        {
            phi[i] = i - 1;
            Prime[++len] = i;
        }
        for(register int j = 1; j <= len && i * Prime[j] <= MAXN; j++)
        {
            NotPrime[i * Prime[j]] = true;
            if(i % Prime[j] == 0)	//此时 Prime[j] 为 i 的最小质因子
			{
            	phi[i * Prime[j]] = phi[i] * prime[j];
				break;
            }
            phi[i * Prime[j]] = phi[i] * (Prime[j] - 1);
        }
    }
    return;
}

线性筛莫比乌斯函数:

inline void Sieve()
{
    miu[1] = 1;
    for(register int i = 2; i <= MAXN; i++)
    {
        if(!NotPrime[i])
        {
            miu[i] = -1;
            Prime[++len] = i;
        }
        for(register int j = 1; j <= len && i * Prime[j] <= MAXN; j++)
        {
            NotPrime[i * Prime[j]] = true;
            if(i % Prime[j] == 0) break;	//此时 miu 函数值为 0,可以直接跳过
            miu[i * Prime[j]] = -miu[i];
        }
    }
    return;
}

可以发现,每个数最多只会被自己的最小质因子筛到一次,所以时间复杂度是线性的 O ( n ) O(n) O(n)。

不过要注意,每个数被自己的最小质因子筛到时,对应的 i * Prime[j]仍然是需要计算函数值的!(当然 μ \mu μ 函数此时就等于 0 0 0,可以直接跳过)


杜教筛

在非线性时间内求解积性函数的前缀和。

设现在我们要求的是 f f f 的前缀和,即 S ( n ) = ∑ i = 1 n f ( i ) S(n) = \sum_{i = 1} ^n f(i) S(n)=∑i=1n​f(i)。

我们考虑把 f f f 卷上一个 g g g 的前缀和,即:
S ′ ( n ) = ∑ i = 1 n ( f ∗ g ) ( i ) = ∑ i = 1 n ∑ d ∣ i f ( d ) g ( i d ) = ∑ d = 1 n g ( d ) ∑ i = 1 ⌊ n d ⌋ f ( i ) = ∑ d = 1 n g ( d ) S ( ⌊ n d ⌋ ) \begin{aligned} S'(n) &= \sum_{i = 1}^n(f * g)(i) \\ &= \sum_{i = 1} ^n \sum_{d | i} f(d)g(\frac{i}{d}) \\ &=\sum_{d = 1}^n g(d) \sum_{i = 1}^{\lfloor\frac{n}{d}\rfloor} f(i) \\ &=\sum_{d = 1}^ng(d)S(\lfloor\frac{n}{d}\rfloor) \end{aligned} S′(n)​=i=1∑n​(f∗g)(i)=i=1∑n​d∣i∑​f(d)g(di​)=d=1∑n​g(d)i=1∑⌊dn​⌋​f(i)=d=1∑n​g(d)S(⌊dn​⌋)​
然后再推导一下,发现:
g ( 1 ) S ( n ) = ∑ d = 1 n g ( d ) S ( ⌊ n d ⌋ ) − ∑ d = 2 n g ( d ) S ( ⌊ n d ⌋ ) g(1)S(n) = \sum_{d = 1}^ng(d)S(\lfloor\frac{n}{d}\rfloor) - \sum_{d = 2}^ng(d)S(\lfloor\frac{n}{d}\rfloor) g(1)S(n)=d=1∑n​g(d)S(⌊dn​⌋)−d=2∑n​g(d)S(⌊dn​⌋)
即:
S ( n ) = ∑ i = 1 n ( f ∗ g ) ( i ) − ∑ d = 2 n g ( d ) S ( ⌊ n d ⌋ ) g ( 1 ) S(n) = \frac{\sum_{i = 1}^n(f * g)(i) - \sum_{d = 2}^ng(d)S(\lfloor\frac{n}{d}\rfloor)}{g(1)} S(n)=g(1)∑i=1n​(f∗g)(i)−∑d=2n​g(d)S(⌊dn​⌋)​
所以,只要我们找到合适的 g g g,就能够快速求出 S ( n ) S(n) S(n) 了。

(这个时候建议看看下文的莫反公式)

对于 μ \mu μ 的前缀和,我们发现有 μ ∗ 1 = ϵ \mu * 1 = \epsilon μ∗1=ϵ,即定义 g = 1 g = 1 g=1,代入公式有 S ( n ) = 1 − ∑ i = 2 n S ( n i ) S(n) = 1 - \sum_{i = 2}^nS(\frac{n}{i}) S(n)=1−∑i=2n​S(in​)。

对于 φ \varphi φ 的前缀和,我们发现有 φ ∗ 1 = i d \varphi * 1 = id φ∗1=id,即定义 g = 1 g = 1 g=1,代入公式有 S ( n ) = n ( n + 1 ) 2 − ∑ i = 2 n S ( n i ) S(n) = \frac{n(n + 1)}{2} - \sum_{i = 2}^n S(\frac{n}{i}) S(n)=2n(n+1)​−∑i=2n​S(in​)。

这些递推式显然可以整除分块做。

不过为了做到非线性复杂度,我们需要先预处理出 n 2 3 n^{\frac{2}{3}} n32​ 以内的 S ( n ) S(n) S(n),然后剩下的用记忆化搜索。

这样的复杂度据说可以达到最快的 O ( n 2 3 ) O(n^{\frac{2}{3}}) O(n32​)。

贴上洛谷模板题(上面举的两个例子)的代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cassert>
#include<unordered_map>

#define MAXN 5000001

using LL = long long;
using namespace std;

int t, n;
int len, Prime[MAXN];
LL Phi[MAXN], Miu[MAXN];
bool NotPrime[MAXN];
unordered_map<int, LL> AnsPhi, AnsMiu;

inline void Prework()
{
	Phi[1] = Miu[1] = 1LL;
	for(register int i = 2; i < MAXN; i++)
	{
		if(!NotPrime[i])
		{
			Prime[++len] = i;
			Phi[i] = i - 1LL;
			Miu[i] = -1LL;
		}
		for(register int j = 1; j <= len && i * Prime[j] < MAXN; j++)
		{
			NotPrime[i * Prime[j]] = true;
			if(i % Prime[j] == 0LL)
			{
				Phi[i * Prime[j]] = Phi[i] * Prime[j];
				break;
			}
			Phi[i * Prime[j]] = Phi[i] * (Prime[j] - 1LL);
			Miu[i * Prime[j]] = -Miu[i];
		}
	}
	for(register int i = 1; i < MAXN; i++) Phi[i] += Phi[i - 1], Miu[i] += Miu[i - 1];
	return;
}

inline LL Solve_Phi(int n)
{
	if(n < MAXN) return Phi[n];
	if(AnsPhi.count(n)) return AnsPhi[n];
	LL ans = 1LL * n * (n + 1LL) / 2LL;
	for(register LL l = 2LL, r; l <= n; l = r + 1LL)
	{
		r = n / (n / l);
		ans -= 1LL * (r - l + 1LL) * Solve_Phi(n / l);
	}
	AnsPhi.insert(make_pair(n, ans));
	return ans;
}

inline LL Solve_Miu(int n)
{
	if(n < MAXN) return Miu[n];
	if(AnsMiu.count(n)) return AnsMiu[n];
	LL ans = 1LL;
	for(register LL l = 2LL, r; l <= n; l = r + 1LL)
	{
		r = n / (n / l);
		ans -= 1LL * (r - l + 1LL) * Solve_Miu(n / l);
	}
	AnsMiu.insert(make_pair(n, ans));
	return ans;
}

int main()
{
	#ifdef FILE
		freopen("Input.in", "r", stdin);
	#endif
	Prework();
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d", &n);
		printf("%lld %lld\n", Solve_Phi(n), Solve_Miu(n));
		continue;
	}
	return 0;
}

M i n _ 25 Min\_25 Min_25 筛

一般用于求积性函数 f f f 的前缀和。 f f f 要求当 p p p 为质数时, f ( p ) f(p) f(p) 是一个次数较少的简单多项式,它的每一项都必须是完全积性函数。

复杂度众说纷纭,不过当 n ≤ 1 0 10 n \le 10^{10} n≤1010 时可以轻松跑过。

  • S t e p    1 Step~~1 Step  1:

先考虑求质数的前缀和:
∑ i = 1 , i ∈ P r i m e n f ( i ) \sum_{i = 1,i \in Prime}^n f(i) i=1,i∈Prime∑n​f(i)

我们先来看一个神奇的操作:

考虑当 d d d 为质数时 f ( p ) f(p) f(p) 的运算公式。

然后,我们把所有数都当成质数,套用这个运算公式!

我们设这个新的函数为 h h h。

前面提到当 p p p 为质数时 f ( p ) f(p) f(p) 是一个次数较低的多项式,所以,我们可以把 h h h 拆成几项,分开来算,最后加在一起即可。

我们设 g ( n , j ) g(n, j) g(n,j) 为 1 ∼ n 1\sim n 1∼n 中,所有质数或者最小质因子大于等于第 j j j 个质数的合数的 f f f 值的和,设 p j p_j pj​ 为第 j j j 个质数。

考虑分讨:

  1. 如果 p j > n p_j > n pj​>n,此时不会再有更多的合数造成负贡献,因此有 g ( n , j ) = g ( n , j − 1 ) g(n, j) = g(n, j - 1) g(n,j)=g(n,j−1);
  2. 如果 p j ≤ n p_j \le n pj​≤n,我们发现包含负贡献的数中一定含有质因子 p j p_j pj​,且除掉 p j p_j pj​ 后最小的质因子会大于等于 p j p_j pj​。所以我们考虑减去 h ( p j ) g ( n p j , j − 1 ) h(p_j)g(\frac{n}{p_j},j−1) h(pj​)g(pj​n​,j−1),但 g ( n p j , j − 1 ) g(\frac{n}{p_j},j−1) g(pj​n​,j−1) 中重复减去了 ∑ i = 1 j − 1 h ( p i ) \sum_{i=1}^{j - 1}h(p_i) ∑i=1j−1​h(pi​) 这些最小质因子小于 p j p_j pj​ 的函数值,所以再把它们加上就好了。

此时有递推式:
g ( n , j ) = g ( n , j − 1 ) − h ( p j ) ( g ( ⌊ n p j ⌋ , j − 1 ) − ∑ i = 1 j − 1 h ( p i ) ) g(n, j) = g(n, j - 1) - h(p_j)(g(\lfloor\frac{n}{p_j}\rfloor,j - 1) - \sum_{i = 1}^{j - 1}h(p_i)) g(n,j)=g(n,j−1)−h(pj​)(g(⌊pj​n​⌋,j−1)−i=1∑j−1​h(pi​))
最后我们可以求出 g ( n , ∣ P r i m e ∣ ) g(n, |Prime|) g(n,∣Prime∣),这就是前缀和中所有质数的贡献。

  • S t e p   2 Step~2 Step 2:

类⽐ g g g,设 S ( n , j ) S(n, j) S(n,j) 表⽰最⼩质因⼦不⼩于第 j j j 个素数的所有 f f f 的函数值和。

初始时有 S ( n , ∣ P r i m e ∣ ) = g ( n , ∣ P r i m e ∣ ) S(n, |Prime|) = g(n, |Prime|) S(n,∣Prime∣)=g(n,∣Prime∣)。

对于质数,我们已经算出答案;对于合数,我们考虑枚举最小质因子及其指数即可递推:

(注意当 f ( p k t + 1 ) f(p_k^{t + 1}) f(pkt+1​) 超出范围时,我们定义其为 0 0 0。)
S ( n , j ) = g ( n , ∣ P r i m e ∣ ) − ∑ i = 1 j − 1 f ( p i ) + ∑ k = j p k 2 ≤ n ∑ t = 1 p k t ≤ n ( f ( p k t + 1 ) + f ( p k t ) S ( ⌊ n p k t ⌋ , k + 1 ) ) ) S(n, j) = g(n, |Prime|) - \sum_{i = 1}^{j - 1} f(p_i) + \sum_{k = j}^{p_k^2 \le n}\sum_{t = 1}^{p_k^{t} \le n} (f(p_k^{t + 1}) + f(p_k^{t}) S(\lfloor\frac{n}{p_k^t}\rfloor, k + 1))) S(n,j)=g(n,∣Prime∣)−i=1∑j−1​f(pi​)+k=j∑pk2​≤n​t=1∑pkt​≤n​(f(pkt+1​)+f(pkt​)S(⌊pkt​n​⌋,k+1)))
前面的 g ( n , j ) − ∑ i = 1 j − 1 f ( p i ) g(n, j) - \sum_{i = 1}^{j - 1} f(p_i) g(n,j)−∑i=1j−1​f(pi​) 即为质数部分,注意要减去所有质数 d   ( d < p j ) d~(d < p_j) d (d<pj​) 的 f ( d ) f(d) f(d) 的贡献;

枚举合数时,我们需要考虑所有最小质因子为 d   ( d ≥ p j ) d~(d \ge p_j) d (d≥pj​) 的数 k k k 对应的 f ( k ) f(k) f(k) 的贡献。由于质数我们在前面已经计算过,因此对于 f ( d t + 1 ) f(d^{t + 1}) f(dt+1) 这一项,我们从 t = 1 t=1 t=1 而非 t = 0 t = 0 t=0 开始枚举。而后面的合数部分,我们类似 g g g 的处理,利用积性函数的性质把 f ( d t ) f(d^t) f(dt) 这一项提出来,同时把 d t d^t dt 这一项消掉,再递归求解所有最小质因子大于 d d d 的 S S S。

最后 S ( n , 0 ) S(n, 0) S(n,0) 即为答案,不过注意要考虑一下 f ( 1 ) f(1) f(1) 的贡献。


说了那么多,拿洛谷的模板题讲解一下吧:

定义积性函数 f ( x ) f(x) f(x),当 p p p 为质数时有 f ( p k ) = p k ( p k − 1 ) f(p^k) = p^k(p^k - 1) f(pk)=pk(pk−1)。

给定 n   ( n ≤ 1 0 10 ) n ~(n \le 10^{10}) n (n≤1010),求:
∑ i = 1 n f ( i ) \sum_{i = 1}^n f(i) i=1∑n​f(i)
对 1 0 9 + 7 10^9 + 7 109+7 取模。

题解:

  • S t e p   1 Step~1 Step 1:

显然对于质数 p p p,有 f ( p ) = p ( p − 1 ) = p 2 − p f(p) = p(p - 1) = p^2 - p f(p)=p(p−1)=p2−p。可以发现,这正是上面提到的 “次数较少的简单多项式”。不看符号,它的每一项都是完全积性函数。

所以我们把 f ( p ) f(p) f(p) 拆成 p 2 p^2 p2 和 p p p 两项计算,最后相减即可。

我们再次回顾递推式:
g ( n , j ) = g ( n , j − 1 ) − h ( p j ) ( g ( ⌊ n p j ⌋ , j − 1 ) − ∑ i = 1 j − 1 h ( p i ) ) g(n, j) = g(n, j - 1) - h(p_j)(g(\lfloor\frac{n}{p_j}\rfloor,j - 1) - \sum_{i = 1}^{j - 1}h(p_i)) g(n,j)=g(n,j−1)−h(pj​)(g(⌊pj​n​⌋,j−1)−i=1∑j−1​h(pi​))
可以发现两件事情:

  1. ⌊ n p j ⌋ \lfloor\frac{n}{p_j}\rfloor ⌊pj​n​⌋ 最多会有 n \sqrt{n} n ​ 种取值,可以考虑整除分块预处理;
  2. n n n 以内的质因子个数也是 n \sqrt{n} n ​ 级别的,所以说 ∑ i = 1 j − 1 h ( p i ) ) \sum_{i = 1}^{j - 1}h(p_i)) ∑i=1j−1​h(pi​)) 也可以预处理。

所以我们先考虑把所有的素数筛出来,同时维护后面的那一项 ∑ i = 1 j − 1 h ( p i ) ) \sum_{i = 1}^{j - 1}h(p_i)) ∑i=1j−1​h(pi​)):

inline void Sieve()
{
	NotPrime[1] = true;
	for(register int i = 2; i <= s; i++)	//s = sqrt(n)
	{
		if(!NotPrime[i])
		{
			Prime[++len] = i;
			sum1[len] = GetMOD(sum1[len - 1] + i) % MOD;
			sum2[len] = GetMOD(sum2[len - 1] + 1LL * i * i % MOD) % MOD;
		}
		for(register int j = 1; j <= len && i * Prime[j] <= s; j++)
		{
			NotPrime[i * Prime[j]] = true;
			if(i % Prime[j] == 0LL) break;
		}
	}
	return;
}

然后我们整除分块,把对应的值存储下来编号,这样可以节省空间。同时我们预处理出 g ( 1 ∼ n , 0 ) g(1 \sim n, 0) g(1∼n,0):

for(register LL l = 1, r; l <= n; l = r + 1)
{
	r = n / (n / l), val[++m] = n / l;
	g1[m] = GetMOD(Prefix1(val[m]) - 1LL + MOD), g2[m] = GetMOD(Prefix2(val[m]) - 1LL + MOD);	//注意要减去 f(1) 的贡献
	if(val[m] <= s) pos1[val[m]] = m; else pos2[n / val[m]] = m;
}

接着我们考虑求解 g g g,这里可以发现后面的值只会由前面推导过来,所以可以考虑把数组滚动:

for(register int i = 1; i <= len; i++)
{
	for(register int j = 1; j <= m && Prime[i] * Prime[i] <= val[j]; j++)
	{
		g1[j] = GetMOD(g1[j] - 1LL * Prime[i] * GetMOD(g1[GetPos(val[j] / Prime[i])] - sum1[i - 1] + MOD) % MOD + MOD); 
		g2[j] = GetMOD(g2[j] - 1LL * Prime[i] * Prime[i] % MOD * GetMOD(g2[GetPos(val[j] / Prime[i])] - sum2[i - 1] + MOD) % MOD + MOD);
	}
}

完毕。

  • S t e p   2 Step~2 Step 2:

接下来考虑求解 S S S。实际上没有那么麻烦,直接暴力枚举然后递归即可:

inline LL S(LL k, int p)
{
	if(Prime[p] > k) return 0LL;
	LL res = GetMOD(GetMOD(g2[GetPos(k)] - g1[GetPos(k)] + MOD) - GetMOD(sum2[p] - sum1[p] + MOD) + MOD);
	for(register int i = p + 1; i <= len && Prime[i] * Prime[i] <= k; i++)
	{
		LL pw = Prime[i];
		for(register int j = 1; pw <= k; pw = pw * Prime[i], j++) res = GetMOD(res + pw % MOD * GetMOD(pw % MOD - 1LL + MOD) % MOD * GetMOD(S(k / pw, i) + (j > 1)) % MOD);
	}
	return res;
}

贴上完整代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cassert>
#include<cmath>

#define MAXN 200005

using LL = long long;
using namespace std;

int len, m, pos1[MAXN], pos2[MAXN];
LL n, s, Prime[MAXN], sum1[MAXN], sum2[MAXN], val[MAXN], g1[MAXN], g2[MAXN];
bool NotPrime[MAXN];
const LL MOD = 1000000007LL, Inv2 = 500000004LL, Inv6 = 166666668LL;

inline LL GetMOD(LL k) { if(k >= MOD) k -= MOD; return k; }
inline LL Prefix1(LL k) { k %= MOD; return k * (k + 1LL) % MOD * Inv2 % MOD; }
inline LL Prefix2(LL k) { k %= MOD; return k * (k + 1LL) % MOD * GetMOD(k * 2LL % MOD + 1LL) % MOD * Inv6 % MOD; }
inline int GetPos(LL k) { if(k <= s) return pos1[k]; return pos2[n / k]; }

inline void Sieve()
{
	NotPrime[1] = true;
	for(register int i = 2; i <= s; i++)
	{
		if(!NotPrime[i])
		{
			Prime[++len] = i;
			sum1[len] = GetMOD(sum1[len - 1] + i) % MOD;
			sum2[len] = GetMOD(sum2[len - 1] + 1LL * i * i % MOD) % MOD;
		}
		for(register int j = 1; j <= len && i * Prime[j] <= s; j++)
		{
			NotPrime[i * Prime[j]] = true;
			if(i % Prime[j] == 0LL) break;
		}
	}
	return;
}

inline LL S(LL k, int p)
{
	if(Prime[p] > k) return 0LL;
	LL res = GetMOD(GetMOD(g2[GetPos(k)] - g1[GetPos(k)] + MOD) - GetMOD(sum2[p] - sum1[p] + MOD) + MOD);
	for(register int i = p + 1; i <= len && Prime[i] * Prime[i] <= k; i++)
	{
		LL pw = Prime[i];
		for(register int j = 1; pw <= k; pw = pw * Prime[i], j++) res = GetMOD(res + pw % MOD * GetMOD(pw % MOD - 1LL + MOD) % MOD * GetMOD(S(k / pw, i) + (j > 1)) % MOD);
	}
	return res;
}

int main()
{
	#ifdef FILE
		freopen("Input.in", "r", stdin);
	#endif
	scanf("%lld", &n), s = (LL)sqrt(n), Sieve();
	for(register LL l = 1, r; l <= n; l = r + 1)
	{
		r = n / (n / l), val[++m] = n / l;
		g1[m] = GetMOD(Prefix1(val[m]) - 1LL + MOD), g2[m] = GetMOD(Prefix2(val[m]) - 1LL + MOD);
		if(val[m] <= s) pos1[val[m]] = m; else pos2[n / val[m]] = m;
	}
	for(register int i = 1; i <= len; i++)
	{
		for(register int j = 1; j <= m && Prime[i] * Prime[i] <= val[j]; j++)
		{
			g1[j] = GetMOD(g1[j] - 1LL * Prime[i] * GetMOD(g1[GetPos(val[j] / Prime[i])] - sum1[i - 1] + MOD) % MOD + MOD); 
			g2[j] = GetMOD(g2[j] - 1LL * Prime[i] * Prime[i] % MOD * GetMOD(g2[GetPos(val[j] / Prime[i])] - sum2[i - 1] + MOD) % MOD + MOD);
		}
	}
	printf("%lld\n", GetMOD(S(n, 0) + 1LL));
	return 0;
}

P o w e r f u l   N u m b e r Powerful~Number Powerful Number

对正整数 k k k 唯一分解为 ∑ p i ∈ P r i m e p i α i \sum_{p_i\in Prime} p_i^{\alpha_i} ∑pi​∈Prime​piαi​​,如满足 ∀ p i ∈ P r i m e , α i ≥ 2 \forall p_i \in {Prime}, \alpha_i \ge 2 ∀pi​∈Prime,αi​≥2,则称其为 P o w e r f u l   N u m b e r Powerful~Number Powerful Number。

每一个 P o w e r f u l   N u m b e r Powerful~Number Powerful Number 都可以表示成 a 2 b 3 a^2 b^3 a2b3 的形式。由此我们也可以证明: 1 ∼ n 1 \sim n 1∼n 内 P o w e r f u l   N u m b e r Powerful~Number Powerful Number 的数量在 O ( n ) O(\sqrt{n}) O(n ​) 级别。

它有什么用处呢?

假设我们要求积性函数 f f f 的前缀和,考虑构造一个前缀和很好求的函数 g g g,满足两函数在质数处值相等。

再设函数 h h h 使得 f = h ∗ g f = h * g f=h∗g, ∗ * ∗ 为狄利克雷卷积。

可以发现,当 d d d 为质数时,有 f ( d ) = h ( d ) + g ( d ) = g ( d ) f(d) = h(d) + g(d) = g(d) f(d)=h(d)+g(d)=g(d)。换句话说,当 d d d 不为 P o w e r f u l   N u m b e r Powerful~Number Powerful Number 时, h ( d ) h(d) h(d) 取值为 0 0 0。

又有:
∑ i = 1 n f ( i ) = ∑ i = 1 n ( h ∗ g ) ( i ) = ∑ i = 1 n ∑ d ∣ i h ( d ) g ( i d ) = ∑ d = 1 n h ( d ) ∑ i = 1 ⌊ n d ⌋ g ( i ) \begin{aligned} \sum_{i = 1}^n f(i) &= \sum_{i = 1}^n (h * g)(i) \\ &=\sum_{i = 1}^n\sum_{d | i} h(d)g(\frac{i}{d}) \\ &=\sum_{d = 1}^nh(d)\sum_{i = 1}^{\lfloor\frac{n}{d}\rfloor} g(i) \end{aligned} i=1∑n​f(i)​=i=1∑n​(h∗g)(i)=i=1∑n​d∣i∑​h(d)g(di​)=d=1∑n​h(d)i=1∑⌊dn​⌋​g(i)​
由于后者前缀和很好求,因此可以考虑暴力找出 n n n 以内的 P o w e r f u l   N u m b e r Powerful~Number Powerful Number,直接暴力加就行。


非特殊函数的筛法

一般思路:

  1. 如果函数满⾜类积性,也就是满⾜对于所有的 a ⊥ b a \perp b a⊥b 都有 f ( a b ) = f ( a ) ⊗ f ( b ) f(ab) = f(a) \otimes f(b) f(ab)=f(a)⊗f(b)( ⊗ \otimes ⊗ 是⼀种运算符)时,可以套⽤积性函数的筛法解决;
  2. 如果函数和其约数密切相关,则试着先从素数整次幂⽅⾯推导,然后拓展到含多种素数的合数上。最后根据推导得到的式⼦进⾏计算。

T o   B e   C o n t i n u e d ⋯ ⋯ \mathit{To~Be~Continued\cdots\cdots} To Be Continued⋯⋯

【论文精读】DisCo: Graph-Based Disentangled Contrastive Learning for Cold-Start Cross-Domain Recommendat 源域中具有相似偏好的用户在目标域中可能会表现出不同的兴趣。因此,直接迁移嵌入可能会引入不相关的源域协同信息。提出了一个基于图的解耦对比学习框架,以捕捉细粒度的用户意图并过滤掉不相关的协同信息,从而避免负迁移。 具体而言,对于每个域,我们使用多通道图编码器来捕捉多样化的用户意图。然后,我们在嵌入空间中构建亲和图,并执行多步随机游走以捕捉高阶用户相似性关系。将一个域视为目标域,我们提出了一种以用户相似性为指导的解耦意图对比学习方法,以优化跨域的用户意图桥接。 阅读详情

相关推荐

爆改YOLOv8 | PSA注意力机制融合入YOLOv8

yolov8,PSA注意力,即插即用

weixin_43986124的博客 2497

筛法求常见积性函数

详解筛法求积性函数。

wanglianda2007的博客 395

写给音响工程师的科普文章(二)——从模电角度去了解调音台话放

    上一篇讲到调音台第一级放大电路为差分放大器,关于差分放大器的基础知识可以参考以下资料:https://wenku.baidu.com/view/e6a46aaa2af90242a995e537 在教材中的差分放大器基本形式是这样子的,在实际电路中把RE电阻换成两个2倍RE,于是变成这样 然后我们现在来对比调音台中的差分放大器和教材中的有什么区别,我就拿yamaha 01V 96来说...

huanghaoAudio的博客 7701

积性函数与筛法

莫比乌斯函数 定义 莫比乌斯函数μ(n)\mu(n)μ(n),当n=1n=1n=1时,μ(n)=1\mu(n)=1μ(n)=1;当n&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;1n&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;1n&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;1时,设nnn的唯一分解式为n=p1c1⋯pkckn=p_1^{c_

又又大柚纸的博客 554

TeraTerm 使用技巧

⏹TeraTerm 使用技巧

菜鸟记录 9992

AI人工智能与TensorFlow的教育科技应用

随着科技的飞速发展,AI人工智能在各个领域都展现出了巨大的潜力,教育领域也不例外。本文章的目的在于深入探讨AI人工智能与TensorFlow在教育科技中的具体应用,包括智能教学系统的开发、学生学习情况的评估与预测等方面。范围涵盖了从基础概念到实际项目应用的各个层面,旨在为教育科技工作者和相关研究者提供全面且深入的技术指导。本文将首先介绍AI人工智能和TensorFlow的核心概念及其联系,让读者对相关技术有基本的了解。接着详细讲解核心算法原理和具体操作步骤,并结合数学模型和公式进行深入分析。

AI智能探索者的博客 1693

15 | 框架思维(下):用筛法求解其他积性函数

上一节,我们讲了素数筛这个算法,并且强调了,要按照框架思维去学习算法代码,因为当你学会这么做的时候,它就可以变成解决多个问题的利器了。本节将带你具体使用素数筛算法框架,去解决一些其他简单的数论问题。通过解决这几个具体问题的过程,我希望你能找到“框架思维”的感觉。

qq_37756660的博客 788

改进爬山算法之六:随机重启爬山算法(Random-Restart Hill Climbing,RRHC)

随机重启爬山算法(Random-Restart Hill Climbing,RRHC)是爬山算法的一种改进策略,是一种启发式搜索算法,旨在通过随机重启来克服传统爬山算法容易陷入局部最优解的局限性,并增加找到全局最优解的概率。随机重启爬山算法通过在多次运行中不断尝试不同的初始解,并在每次运行中执行标准的爬山算法,最终选择所有运行中找到的最佳解作为最终结果。(1)计算复杂度:由于需要多次运行爬山算法,因此算法的计算复杂度可能较高,特别是在解空间较大或每次迭代需要较长时间的情况下。

搏博的专栏 2205

欧拉筛筛积性函数

Part. 1 Preface 这个东西是我在做 JZPTAB 的时候 LYC 给我讲的。 然后发现这是个通法,就写一写。 本文除了例题所有代码均未经过编译,仅作为参考。 Part. 2 Untitled(怎么取标题呀)(哦 正文) Part. 2-1 Worse ver. 对于一个积性函数 f(n)f(n)f(n),如果我们已知 f(1),f(p),f(pk)f(1),f(p),f(p^{k})f(1),f(p),f(pk) (ppp 是一个素数)并且可以在 O(log⁡2(n))O(\log_{2}(n

root的博客 316

Autosar MCAL-S32K324 Fls配置-基于EB

Autosar MCAL Flash配置,基于S32K324,EB软件

赞的博客 1513

如何使用Python爬取并保存网页上的所有附件

爬虫(Crawler)是一种自动化程序,它通过模拟人工访问网页并下载网页上的数据或文件,常用于数据采集和分析。爬虫通常以“爬取”的形式自动浏览和提取网页中的各种信息。本文详细介绍了如何使用Python爬取并保存网页中的附件文件,涵盖了网页解析、文件下载、多线程与异步优化等内容。希望通过本文的讲解,能够帮助你理解并掌握附件爬取的技巧。在实际应用中,爬虫开发不仅需要解决技术问题,还要遵守网络道德和法律法规,避免不当使用爬虫造成负面影响。

2201_76125261的博客 965

欧拉函数与积性函数

对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目。欧拉筛法就是求出小于等于n的所有素数的方法。

m0_69700299的博客 1078

LVGL(lv_canvas)

这篇文章介绍了LVGL图形库中的lv_canvas组件,主要内容包括: lv_canvas是一个绘图缓冲区对象,提供丰富的绘图API,可以理解为LVGL的"离屏画布"。 基本使用流程:创建缓冲区->创建canvas对象->设置缓存和尺寸->进行绘图操作。 详细列举了常用的绘图函数,如绘制线条、矩形、文字、图片等。 提供了示例代码展示如何创建画布并绘制一条红色线段。 介绍了canvas的典型应用场景,如动态波形绘制、自定义控件底图等。 对比了canvas与普通img的区别

m0_49476241的博客 1024

欧拉筛法和积性函数

众所周知,求素数可以使用筛法,代码大概是这样子for(int i=2;i<N;i++){ if(!isp[i]){ pri[tot++]=i; for(int j=i+i;j<N;j+=i) isp[j]=1; } } 这样子的复杂度怎么算呢,可以粗略估计,内层for循环只会在ii是素数的时候进行,因此,总的计算次数大约是∑k

Your magic is mine 952

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

欧拉筛法与积性函数

欧拉筛法普通的筛法,即Eratosthenes筛法,复杂度为O(nloglogn)O(nloglogn),当范围大时,就扛不住了,需要利用欧拉筛法,也称线性筛法,下面还会介绍利用欧拉筛法的优美性质来求积性函数。 先给出欧拉筛法的代码:for(int i=2;i<=n;i++){ if(vis[i])prime[++prime[0]]=i; for(int j=1;j<=prime[

Sdywolf的博客 605

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

线性筛,积性函数,狄利克雷卷积,常见积性函数的筛法

一些性质 积性函数:对于函数f(n)f(n),若满足对任意互质的数字a,b,a∗b=na,b,a*b=n且f(n)=f(a)f(b)f(n)=f(a)f(b),那么称函数f为积性函数。 狄利克雷卷积:对于函数f,g,定义它们的卷积为 (f∗g)(n)=∑d|nf(d)g(nd)(f∗g)(n)=\sum_{d|n}f(d)g(\frac{n}{d})。 狄利克雷卷积满足很多性质: 交换律

Cyhlnj 1107

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

pku 3604 Professor Ben 积性函数加筛法

这题是应用积性函数的性质来做的(我不会证明)!因为数据量很大,时间卡的很死(不像应用积性函数的pku2480),所以还要用筛法优化才能过!附代码:Memory: 39344K Time: 2641MS #include using namespace std;__int64 cube[32], F[5000010];void solve(){ __

qjklw 570

一种求特殊积性函数前缀和的筛法——Extended Eratosthenes Sieve

序 关于这个筛法,此前是在学习了杜教筛的基础上的,杜教筛有良好的复杂度O(n23)O(n^\frac{2}{3})O(n32​),而且实现简单。唯一的不足之处就是函数本身需要满足一些特殊的条件(即我们需要找另一个积性函数,使其与所求函数的狄利克雷卷积是一个很容易求前缀的式子),而且与函数本身的积性性质没有太大关系。后来接触到洲阁筛,这种筛法是一种解决更加广泛的积性函数前缀和,但是实在不太好写(因为...

Coldfresh的博客 581

筛法,欧拉函数表,莫比乌斯函数表,积性函数

递推 const int maxn=1000010; for(int i=1; i<=maxn; i++) if(i&1) p[i]=i; else p[i]=(i>>1); for(int i=3; i<=maxn; i+=2) if(p[i]==i) { for(j=i; j<=maxn; j+=i) p[j]=p[j]

a7f650ebd327889c的博客 423

【模板】埃拉托色尼筛法 && 欧拉筛法 && 积性函数

埃拉托色尼筛法   朴素算法 1 vis[1]=1; 2 for (int i=2;i<=n;i++) 3 if (!vis[i]) 4 { 5 pri[++tot]=i; 6 for (int j=i*2;j<=n;j+=i) 7 vis[j]=1; 8 } 欧拉...

weixin_30502157的博客 184

今天让我们来学一学积性函数的筛法

第一件事情我们要知道的是:积性函数都可以线性筛。(就是说复杂度是O(n)O(n)O(n) 积性函数的性质不说了,在说说常见的几个积性函数。 μ(n)μ(n)\mu(n):莫比乌斯函数 ϕ(n)ϕ(n)\phi(n):欧拉函数 d(n)d(n)d(n):一个数n的约数个数 σ(n)σ(n)σ(n):一个数n的约数和 那我们就来筛筛看吧,(注意:所有线性筛积性函数都必须基于线性筛素数。 ...

Coldfresh的博客 586

积性函数求和问题的一种筛法

本文介绍了一种 $ O(n^{2/3} \log^{-1} n) $ 的积性函数求和方法。

whzzt的博客 5325

线性筛法与积性函数

神犇的博客:浅谈一类积性函数的前缀和 线性筛法//线性筛法求质数。 memset(mark,0,sizeof(mark)); pt=0; for(int i=2;i<=N;i++) { if(!mark[i]){ prime[pt++]=i; mark[i]=i; } for(int j=0,u=dmin(mark[i],N/i);j<pt&&

IceShip Space 1005

信息学奥赛一本通 2040:【例5.7】从筛法到积性函数:欧拉筛的进阶应用

本文深入探讨了欧拉筛在信息学奥赛中的进阶应用,从基础的质数筛选到积性函数的高效计算。通过对比普通筛法与欧拉筛的效率差异,结合实战案例和优化技巧,展示了欧拉筛如何在线性时间内处理复杂数论问题,如欧拉函数和约数平方和的计算,为算法竞赛选手提供强大工具。

weixin_30899023的博客 277

线性筛法及积性函数总结(欧拉函数、莫比乌斯函数、约数和函数、约数个数函数)...

  线性筛法在数论中起着至关重要的作用,对于一部分求解有关积性函数的问题可以大大降低时间复杂度。线性筛法中,除了线性筛质数,所要筛的函数必须是积性函数,而线性筛这些函数的基础也是线性筛质数。先来解释一下什么是积性函数?积性函数就是指对于一个函数f,f(1)=1且对于任意两个互质的数x,y满足f(x)*f(y)=f(x*y)。而如果任意两个数x,y都满足以上等式,那么这个函数就是完全积性函数。 常...

weixin_33727510的博客 426

浅谈积性函数的线性筛法

前置知识 数论函数及相关基本定义 素数的线性筛 线性筛 线性筛可以在严格$O(n)$的时间内筛出积性函数的值, 它有常见的套路 假设$n = p_1^{a_1} p_2^{a_2} \dots p_k^{a_k}$ 如果我们能快速得出$f(p_i)$和$f(p_i^{k+1})$的取值,那么直接套板子就行了 在下文中如无特殊说明,默认$p_i$表示$n$质因数分解之后第$i$个质数...

weixin_34348805的博客 222
上一篇: OI数学小记
下一篇: 函数与表达式的反演变换
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值