前言
想感受一下敲公式的快感,于是有了这篇文章。
只有一些最简单的应用。
杜教筛
可以在低于线性的时间内筛积性函数前缀和,下文介绍原理:
假设我们要求 $S(n)=\sum_{i=1}^nf(i)$,其中 $f(x)$ 是个积性函数。
然后对于任意一个积性函数 $g(x)$,都有: $$ \begin{aligned} \sum\limits_{i=1}^n(f*g)(i) =&\sum\limits_{i=1}^n\sum\limits_{d\mid i}g(d)f(i/d)\\ =&\sum\limits_{d=1}^ng(d)\sum\limits_{d\mid i}^nf(i/d)\\ =&\sum\limits_{d=1}^ng(d)S(\lfloor n/d\rfloor)\\ =&\sum\limits_{i=1}^ng(i)S(\lfloor n/i\rfloor) \end{aligned} $$
接下来有:
$$ g(1)S(n)=\sum\limits_{i=1}^n(f*g)(i)-\sum_{i=2}^ng(i)S(\lfloor n/i\rfloor) $$ 需要构造合适的函数 $g(x)$ 来快速求解前一项,同时用整除分块计算后一项。
实现方式是先筛出来这个函数的前 $n^{\frac{2}{3}}$ 项,然后比这个数大的采用递归计算。用哈希表记忆化后的复杂度是 $O(n^{\frac{2}{3}})$,我不会证明。
luogu4213 sum
Description
求 $\sum_{i=1}^n\mu(i),\sum_{i=1}^n\varphi(i)$,$n<2^{31}$。
Solution
$\mu*1=\epsilon$,第一个问题做完了。
第二个虽然可以用 $id=\varphi*1$ 做,但是用莫比乌斯反演更快一些:先求出 $\sum_{i=1}^n\sum_{j=1}^n[\gcd(i,j)=1]$,简单莫反得到这个东西等于 $\sum_{i=1}^n\mu(i)\lfloor n/d\rfloor^2$,求出 $\mu$ 的前缀和之后可以直接 $O(\sqrt n)$ 求解。
然后要求的答案是 $\sum_{i=1}^n\sum_{j=1}^i[\gcd(i,j)=1]$,把刚刚的答案除以 $2$,再考虑一下 $i=j=1$ 就做完了。
Code
typedef long long ll;
typedef unsigned long long ull;
namespace solve
{
const int maxn = 1e7 + 10;
long long pri[maxn], phi[maxn], vis[maxn], mu[maxn], tot;
void init(const int n = 5e6)
{
mu[1] = phi[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!vis[i])
pri[++tot] = i, mu[i] = -1, phi[i] = i - 1;
for (int j = 1; j <= tot && i * pri[j] <= n; j++)
{
vis[i * pri[j]] = 1;
if (i % pri[j] == 0)
{
mu[i * pri[j]] = 0;
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
else
{
mu[i * pri[j]] = -mu[i];
phi[i * pri[j]] = phi[i] * (pri[j] - 1);
}
}
}
for (int i = 2; i <= n; i++)
mu[i] += mu[i - 1], phi[i] += phi[i - 1];
}
unordered_map<int, long long> mp;
int sum_mu(int n)
{
if (n <= 5e6)
return mu[n];
if (mp.count(n))
return mp[n];
long long res = 1;
for (long long i = 2, j; i <= n; i = j + 1)
{
j = n / (n / i);
res -= 1ll * (j - i + 1) * sum_mu(n / i);
}
return mp[n] = res;
}
long long sum_phi(int n)
{
if (n <= 5e6)
return phi[n];
long long res = 0;
for (long long i = 1, j; i <= n; i = j + 1)
{
j = n / (n / i);
res += (sum_mu(j) - sum_mu(i - 1)) * 1ll * (n / i) * (n / i);
}
return (res + 1) / 2;
}
void main()
{
init();
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
cout << sum_phi(n) << " " << sum_mu(n) << endl;
}
}
}
luogu3768 简单的数学题
Description
求: $$ \sum_{i=1}^n\sum_{j=1}^nij\gcd(i,j) $$ 对 $p$ 取模的结果。$n\leq10^{10}$。
Solution
不说啥了,直接开始推。 $$ \begin{aligned} ans =&\sum_{i=1}^n\sum_{j=1}^n\sum_{d=1,d|i,d|j}ijd[\gcd(i,j)=d]\\ =&\sum_{d=1}^nd\sum_{i=1,d|i}^ni\sum_{j=1,j|i}^nj[\gcd(i,j)=d]\\ =&\sum_{d=1}^nd^3\sum_{i=1}^{\lfloor n/d\rfloor}i\sum_{j=1}^{\lfloor n/d\rfloor}j[\gcd(i,j)=1]\\ =&\sum_{d=1}^nd^3\sum_{i=1}^{\lfloor n/d\rfloor}i\sum_{j=1}^{\lfloor n/d\rfloor}j\sum_{k|i,k|j}\mu(k)\\ =&\sum_{d=1}^nd^3\sum_{k=1}^{\lfloor n/d\rfloor}\mu(k)\sum_{i=1,k|i}^{\lfloor n/d\rfloor}i\sum_{j=1,k|j}^{\lfloor n/d\rfloor}j \end{aligned} $$ 设 $f(x)=\dfrac{x^2(x+1)^2}{4}$。 $$ \begin{aligned} ans=&\sum_{d=1}^nd^3\sum_{k=1}^{\lfloor n/d\rfloor}\mu(k)k^2f(\lfloor\dfrac{n}{dk}\rfloor) \end{aligned} $$ 设 $T=dk$。 $$ \begin{aligned} ans &=\sum_{d=1}^nd\sum_{T=1,d|T}^{\lfloor n/d\rfloor}\mu(\dfrac{T}{d})T^2f(\lfloor\dfrac{n}{T}\rfloor)\\ &=\sum_{T=1}^nf(\lfloor\dfrac{n}{T}\rfloor)T^2\sum_{d|T}d\times\mu(\dfrac{T}{d}) \end{aligned} $$ 由 $id*\mu=\varphi$,有: $$ ans=\sum_{T=1}^nf(\lfloor\dfrac{n}{T}\rfloor)T^2\varphi(T) $$ 设 $g(x)=x^2\varphi(x)$,我们需要求出它的前缀和,开始杜教筛。
设 $h(x)=x^2$,那么 $(h\ast g)(n)=n^2\sum_{d|n}\varphi(d)$,又由于 $\varphi\ast 1=id$,有 $(h\ast g)(x)=x^3$,这玩意的前缀和很好算。
然后就做完了。
Code
namespace solve
{
const int maxn = 1e7 + 10;
int mod;
int vis[maxn];
ll phi[maxn];
int pri[maxn], tot;
ll n;
void init(const int n = 1e7)
{
phi[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!vis[i])
pri[++tot] = i, phi[i] = i - 1;
for (int j = 1; j <= tot && i * pri[j] <= n; j++)
{
vis[i * pri[j]] = 1;
if (i % pri[j])
phi[i * pri[j]] = phi[i] * phi[pri[j]];
else
{
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
}
}
for (int i = 2; i <= n; i++)
phi[i] = 1ll * i * i % mod * phi[i] % mod, phi[i] += phi[i - 1], phi[i] %= mod;
}
ll qpow(ll a, ll x, ll p)
{
ll res = 1;
for (; x; x >>= 1, a = a * a % p)
if (x & 1)
res = res * a % p;
return res;
}
ll S(ll x) { return x * (x + 1) / 2 % mod; }
inline ll f(ll x) { return S(x % mod) * S(x % mod) % mod; }
ll inv6;
inline ll h(ll x)
{
x %= mod;
return 1ll * x * (x + 1) % mod * (2 * x + 1) % mod * inv6 % mod;
}
unordered_map<ll, ll> mp;
ll sum(ll n)
{
if (n <= 1e7)
return phi[n];
if (mp.count(n))
return mp[n];
ll res = f(n);
for (ll i = 2, j; i <= n; i = j + 1)
{
j = n / (n / i);
res -= (h(j) - h(i - 1) + mod) % mod * sum(n / i) % mod;
res %= mod;
}
return mp[n] = (res + mod) % mod;
}
void main()
{
cin >> mod >> n;
init();
inv6 = qpow(6, mod - 2, mod);
ll ans = 0;
for (ll i = 1, j; i <= n; i = j + 1)
{
j = n / (n / i);
ans += (sum(j) - sum(i - 1)) % mod * f(n / i) % mod;
ans %= mod;
}
ans += mod, ans %= mod;
cout << ans << endl;
}
}
NOI2016 循环之美
暂时不会推,先咕了。