以前作過很多的數論題,關於莫比烏斯與積性函數的數論題挺多的。。。特意過來總結一下。。看成本身的一個回顧了-_-php
先安利一下神犇tls的博客和神犇PoPoQQQ的pdf !
膜拜tls…
跪popoqqq…
還有IOI金牌神犇任之州的集訓隊論文,都是好文啊!html
須要先知道線性篩這個東西。。
orz…
線性篩的思想是每一個合數都只會被它最小的質因數篩去,經過線性篩,咱們能夠O(n)獲得1到n內一些數論函數的值,好比說歐拉函數、莫比烏斯函數、因子個數等等。。。c++
本文的除法均爲整除,除非特別指出。
[expresion]爲bool表達式,值爲0或1,當且僅當expresion爲真時爲1,不然爲0。
(i,j)表示gcd(i,j)。git
首先定義莫比烏斯函數
web
莫比烏斯函數的性質:數組
1)
有了上述這個式子,咱們就能夠直接簡單地以O(nlogn)篩出1到n內全部數的莫比烏斯函數值了~
至此咱們已經有兩種辦法求1到n內全部數的歐拉函數值了。svg
//O(n)
bool vis[N];
int primes[N], miu[N];
int init(int n) {
int tot = 0;
miu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
primes[tot++] = i;
miu[i] = -1;
}
for (int j = 0; j < tot; j++) {
int k = i * primes[j];
if (k > n)break;
vis[k] = true;
if (i % primes[j]) miu[k] = -miu[i];
else break;
}
}
}
//O(nlogn)
void init(int n) {
miu[1] = 1;
int t = n >> 1;
for (int i = 1; i <= t; i++) if (miu[i]) {
for (int j = i << 1; j <= n; j += i) miu[j] -= miu[i];
}
}
2)
莫比烏斯函數除了能夠用在莫比烏斯反演中以外,還能夠用來進行容斥。
舉一個常見的例子,求取1到n這個區間內有多少個數與x互質,通常的作法是直接進行容斥,可是咱們能夠發現容斥的係數恰好是莫比烏斯函數,即ans =
莫比烏斯反演是一個這樣的式子:
定義
莫比烏斯反演還有一種更常見的形式:
通常應用的都是上述的第二種形式,證實能夠經過概括得出,也能夠直接經過式子變換得出,還能夠由狄利克雷卷積證實。
上述的證實中給出了一種常見的和式變換技巧:交換求和順序。經過交換求和順序,咱們每每能夠將某些和式化簡或者更容易求出該和式,後面公式的化簡將屢次用到。學習
莫比烏斯反演還有一個推廣式,以下:
設
常見的積性函數有:
因子個數函數
徹底積性函數有:
元函數
咱們來看看積性函數的應用:
若是
舉個簡單的例子:因子個數
積性函數還能夠用來進行線性篩從而獲得不少數論函數的值~
好比下面的歐拉函數:
能夠知道當
const int N = 1e6 + 5;
bool vis[N];
int phi[N], p[N], cnt = 0;
void seive() {
cnt = 0;
phi[1] = 1;
for (int i = 2; i < N; i++) {
if (!vis[i]) p[cnt++] = i, phi[i] = i - 1;
for (int j = 0; j < cnt; j++) {
int s = i * p[j];
if (s > N) break;
vis[s] = 1;
if (i % p[j] == 0) {
phi[s] = p[j] * phi[i];
break;
}
phi[s] = (p[j] - 1) * phi[i];
}
}
}
積性函數的和也是積性函數,積性函數前綴和是一個常見的問題,經常須要低於線性時間內解決。
狄利克雷卷積是解決積性函數前綴和問題的一個重要工具。
對兩個算術函數f, g,定義其Dirichlet卷積爲新函數f * g,知足
狄利克雷卷積知足如下定律:
若f,g均爲積性函數,則f*g也是積性函數。
咱們能夠O(nlogn)預處理兩個函數的Dirichlet卷積。
LL f[N], g[N], h[N];
void calc(int n) {
for (int i = 1; i * i <= n; i++) {
h[i * i] += f[i] * g[i];
for (int j = i + 1; i * j <= n; j++) h[i * j] += f[i] * g[j] + f[j] * g[i];
}
}
不少數論函數均可以用狄利克雷卷積來表示:
①約數個數函數
②約數和函數
③
④
如今咱們能夠經過狄利克雷卷積來證實莫比烏斯反演了:
若是
設f(n)爲一個數論函數,須要計算
要解決上述問題,咱們能夠構造一個
找到一個合適的數論函數g(n),
下面給出求積性函數前綴和時經常使用到的一些公式和結論:
①
②
③
④
⑤
⑥
bzoj 2420 徹底平方數
傳送門
題意:求第n個無平方因子數。
分析:問題等價於求最小的x使得1到x內有n個無平方因子數,咱們考慮容斥,總數減掉2^2的倍數個數,3^2的倍數個數,5^2的倍數個數….加上(2*3)^2的倍數個數,加上(3*5)^2的倍數個數,加上(3*5)^2的倍數個數…..能夠發現容斥的係數就是莫比烏斯函數。這樣咱們二分x,而後
不過注意到m/(i*i)的值是分塊的,因此咱們能夠分塊求,因此複雜度下界大概是
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 41005;
bool vis[N];
int primes[N], miu[N];
int init(int limit) {
int tot = 0;
miu[1] = 0;
for (int i = 2; i <= limit; i++) {
if (!vis[i]) {
primes[tot++] = i;
miu[i] = 1;
}
for (int j = 0; j < tot; j++) {
int k = i * primes[j];
if (k > limit) break;
vis[k] = true;
if (i % primes[j]) miu[k] = -miu[i];
else break;
}
miu[i] += miu[i-1];
}
}
LL cal(LL m) {
LL s = 0;
for (LL i = 1, last; i * i <= m; i = last + 1) {
LL x = m / (i * i); last= sqrt(m / x);
s += (miu[last] - miu[i-1]) * x;
}
return m - s;
}
int main() {
init(41000);
int t;
scanf("%d", &t);
while (t--) {
LL k;
scanf("%lld", &k);
LL l = 0, r = 1644934090LL;
while (r - l > 1) {
LL m = (r + l) >> 1;
cal(m) >= k ? r = m : l = m;
}
printf("%lld\n", r);
}
return 0;
}
bzoj 2301 Problem b
傳送門
題意:對於給出的 n 個詢問,每次求有多少個數對 (x,y) ,知足 a ≤ x ≤ b , c ≤ y ≤ d ,且 gcd(x,y) = k , gcd(x,y) 函數爲 x 和 y 的最大公約數。
分析:咱們考慮容斥原理分紅四段求,那麼如今問題變爲了求1~n/k,1到m/k內有多少對數(x,y)最大公約數爲1了。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e4 + 5;
bool vis[N];
int primes[N], miu[N], sum[N];
int init(int n) {
int tot = 0;
sum[1] = miu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
primes[tot++] = i;
miu[i] = -1;
}
for (int j = 0; j < tot; j++) {
int k = i * primes[j];
if (k > n)break;
vis[k] = true;
if (i % primes[j]) miu[k] = -miu[i];
else break;
}
sum[i] = sum[i-1] + miu[i];
}
}
LL solve(int n, int m) {
if (n > m) swap(n, m);
LL ans = 0;
for (int i = 1, last; i <= n; i = last + 1) {
last = min(n / (n / i), m / (m / i));
if (sum[last] - sum[i-1]) ans += (LL)(n / i) * (m / i) * (sum[last] - sum[i-1]);
}
return ans;
}
int main() {
init(5e4);
int t;
scanf("%d", &t);
while (t--) {
int a, b, c, d, k;
scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
a--; c--;
a /= k; b /= k; c /= k; d /= k;
printf("%lld\n", solve(b, d) + solve(a, c) - solve(a, d) - solve(c, b));
}
return 0;
}
bzoj 2820 YY的GCD
傳送門
題意:給定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)爲質數的(x, y)有多少對。
分析:按照上題的套路,咱們知道
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7 + 5;
bool vis[N];
int primes[N], miu[N], s[N];
void init(int n) {
int tot = 0;
miu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
primes[tot++] = i;
miu[i] = -1;
}
for (int j = 0; j < tot; j++) {
int k = i * primes[j];
if (k > n)break;
vis[k] = true;
if (i % primes[j]) miu[k] = -miu[i];
else break;
}
}
for (int i = 0; i < tot; i++) {
for (int j = primes[i], k = 1; j <= n; j += primes[i], k++) s[j] += miu[k];
}
for (int i = 1; i <= n; i++) s[i] += s[i-1];
}
int main() {
init(1e7);
int T;
scanf("%d", &T);
while (T--) {
LL n, m;
scanf("%lld %lld", &n, &m);
if (n > m) swap(n, m);
LL ans = 0;
for (int i = 1, last; i <= n; i = last + 1) {
last = min(n / (n / i), m / (m / i));
if (s[last] - s[i - 1]) ans += (n / i) * (m / i) * (s[last] - s[i - 1]);
}
printf("%lld\n", ans);
}
return 0;
}
bzoj 3529 數表
傳送門
題意:給q個詢問,每一個詢問給出n,m,a,求
分析:出題人相比上面幾題加了一點套路而已-_-。
對於這種帶有限制的計數問題,若是很差直接解決,咱們能夠先直接分析,而後在考慮限制,後面每每能夠直接算知足限制的,或者容斥,固然這裏與容斥無關。。
先無論a,令ans = \sum_{i=1}^{n}\sum_{j=1}^{m}F((i, j))=\sum_{g=1}^{min(n,m)}F(g) * num(g)
ans = \sum_{i=1}^{n}\sum_{j=1}^{m}F((i, j))=\sum_{g=1}^{min(n,m)}F(g) * \sum_{i=1}^{min(\frac{n}{g},\frac{m}{g})}u(i) * \frac{n}{gi}*\frac{m}{gi}
#include <bits/stdc++.h>
using namespace std;
const int N = 100001, mod = 0x7fffffff;
bool vis[N];
int p[N], g[N], miu[N], tmp[N], ans[N];
struct que{
int n, m, a, id;
bool operator < (const que& x) const { return a < x.a; }
}q[20005];
struct dsum{
int s, id;
bool operator < (const dsum& x) const { return s < x.s; }
}d[N];
int qpow(int x, int n) {
int res = 1;
while (n) {
if (n & 1) res *= x;
x *= x; n >>= 1;
}
return res;
}
void init(int n) {
miu[1] = 1; int k = 0;
d[1].id = d[1].s = 1;
for (int i = 2; i <= n; i++) {
d[i].id = i;
if (!vis[i]) p[k++] = i, g[i] = 1, tmp[i] = d[i].s = 1 + i, miu[i] = -1;
for (int j = 0; j < k; j++) {
int m = i * p[j];
if (m > n) break;
vis[m] = 1;
if (i % p[j] == 0) {
g[m] = g[i] + 1;
tmp[m] = tmp[i] + qpow(p[j], g[m]);
d[m].s = d[i].s + d[i].s / tmp[i] * (tmp[m] - tmp[i]);
break;
}
miu[m] = -miu[i];
g[m] = 1; tmp[m] = 1 + p[j];
d[m].s = d[i].s * tmp[m];
}
}
sort(d + 1, d + N);
}
namespace BIT {
int c[N+100];
inline void add(int x, int y) {
for (; x < N; x += x & -x) c[x] += y;
}
inline int get(int x) {
int res = 0;
for (; x; x -= x & -x) res += c[x];
return res;
}
}
int main() {
init(100000);
int Q;
scanf("%d" ,&Q);
for (int i = 0; i < Q; i++) {
q[i].id = i;
scanf("%d %d %d", &q[i].n, &q[i].m, &q[i].a);
if (q[i].n > q[i].m) swap(q[i].n, q[i].m);
}
sort(q, q + Q);
for (int i = 0, cur = 1; i < Q; i++) {
while (cur < N && d[cur].s <= q[i].a) {
for (int j = 1; j * d[cur].id < N; j++) if (miu[j]) BIT::add(j * d[cur].id, d[cur].s * miu[j]);
cur++;
}
int res = 0, n = q[i].n, m = q[i].m;
for (int j = 1, last; j <= n; j = last + 1) {
last = min(n / (n / j), m / (m / j));
res += (BIT::get(last) - BIT::get(j - 1)) * (n / j) * (m / j);
}
ans[q[i].id] = res & mod;
}
for (int i = 0; i < Q; i++) printf("%d\n", ans[i]);
return 0;
}
bzoj 2693 jzptab
傳送門
題意:多組詢問,求
分析:先將lcm轉化爲熟悉的gcd,咱們有
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10000005;
const LL mod = 100000009;
bool vis[N];
int miu[N], p[N];
LL g[N];
int init(int n) {
int tot = 0;
miu[1] = 1; g[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
p[tot++] = i;
miu[i] = -1;
if ((g[i] = (i - (LL)i * i % mod)) < 0) g[i] += mod;
}
for (int j = 0; j < tot; j++) {
int k = i * p[j];
if (k > n) break;
vis[k] = true;
if (i % p[j] == 0) { g[k] = p[j] * g[i] % mod; break;}
LL t = p[j] - (LL)p[j] * p[j] % mod;
if (t < 0) t += mod;
g[k] = g[i] * t % mod;
miu[k] = -miu[i];
}
}
for (int i = 1; i <= n; i++) {
g[i] += g[i-1];
if (g[i] >= mod) g[i] -= mod;
}
}
int main() {
init(1e7);
int t;
scanf("%d", &t);
while (t--) {
LL n, m;
scanf("%lld %lld", &n, &m);
LL ans = 0;
if (n > m) swap(n, m);
for (LL i = 1, last; i <= n; i = last + 1) {
LL x = n / i, y = m / i;
last = min(n / x, m / y);
x = x * (x + 1) / 2 % mod; y = y * (y + 1) / 2 % mod;
ans = (ans + ((g[last] - g[i-1] + mod) % mod * (x * y % mod)) % mod) % mod;
}
printf("%lld\n", ans);
}
return 0;
}
hdu 6053 TrickGCD
傳送門
題意:給定數組A,求有多少個不一樣的數組B知足
分析:相信作完上面的題目以後很容易分析得出ans = \sum_{T=1}^{min(A_1,A_2,...A_n)}-u(T)*\prod_{i=1}^{n}\frac{A_i}{T}
此處分塊沒任何意義,咱們考慮暴力枚舉T,快速計算\prod_{i=1}^{n}\frac{A_i}{T}
另外咱們也能夠經過容斥原理獲得上述的式子,此處只須要考慮無平方因子數便可,咱們加上以2爲公約數的序列個數,加上以3爲公約數的序列個數,加上以5爲公約數的序列個數….減去以2*3爲公約數的序列個數,減去以3*5爲公約數的序列個數,減去以2*5爲公約數的序列個數…能夠發現容斥的係數恰好是莫比烏斯函數值的相反數。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
const LL mod = 1e9 + 7;
bool vis[N];
int prime[N], miu[N], a[N], cnt[N<<1];
void init(int n) {
miu[1] = -1;
for (int i = 1; i <= n; i++) if (miu[i]) {
for (int j = i << 1; j <= n; j += i) miu[j] -= miu[i];
}
}
LL qpow(LL x, LL a) {
LL res = 1;
while (a) {
if (a & 1) res = res * x % mod;
x = x * x % mod;
a >>= 1;
}
return res;
}
int main() {
init(1e5);
int t, ca = 0;
scanf("%d", &t);
while (t--) {
int n, mi = 100000;
scanf("%d", &n);
for (int i = 0; i <= 200000; i++) cnt[i] = 0;
while (n--) {
int x;
scanf("%d", &x);
cnt[x]++;
mi = min(mi, x);
}
LL ans = 0;
for (int i = 1; i <= 200000; i++) cnt[i] += cnt[i - 1];
for (int i = 2; i <= mi; i++) if (miu[i]) {
LL s = 1;
for (int j = i << 1, k = 2; j <= 100000; j += i, k++) s = s * qpow(k, cnt[j + i - 1] - cnt[j - 1]) % mod;
ans = (ans + s * miu[i] + mod) % mod;
}
printf("Case #%d: %lld\n", ++ca, ans);
}
return 0;
}
hdu 5382
傳送門
題意:定義F(n)=\sum_{i=1}^{n}\sum_{j=1}^{n}[lcm(i,j)+gcd(i,j)>=n]
分析:考慮F(n)
令f(n)=\sum_{i=1}^{n}\sum_{j=1}^{n}[lcm(i,j)+gcd(i,j)==n]
f(n)=\sum_{i=1}^{n}\sum_{j=1}^{n}[\frac{i*j}{gcd(i,j)}+gcd(i,j)==n]=\sum_{g=1}^{n}\sum_{i=1}^{\frac{n}{g}}\sum_{j=1}^{\frac{n}{g}}[(i,j)==1]*[g(ij+1)==n] =\sum_{g|n}\sum_{i=1}^{\frac{n}{g}}\sum_{j=1}^{\frac{n}{g}}[i*j==\frac{n}{g}-1][(i,j)==1]=\sum_{g|n}2^{G(\frac{n}{g}-1)}
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 5, mod = 258280327;
int f[N], g[N], h[N], pri[N], p[20];
bool vis[N];
void init(int n) {
g[1] = p[0] = 1;
for (int i = 1; i <= 20; i++) p[i] = p[i-1] << 1;
int k = 0;
for (int i = 2; i <= n; i++) {
if (!vis[i]) pri[k++] = i, g[i] = 1;
for (int j = 0; j < k; j++) {
int s = i * pri[j];
if (s > n) break;
vis[s] = 1;
if (i % pri[j] == 0) { g[s] = g[i]; break; }
g[s] = g[i] + 1;
}
g[i] = p[g[i]];
}
for (int i = 1; i <= n; i++) {
for (int j = i << 1, k = 2; j <= n; j += i, k++) {
h[j] += g[k-1];
if (h[j] >= mod) h[j] -= mod;
}
}
for (int i = 1; i <= n; i++) {
f[i] = f[i-1] + 2 * i - 1 - h[i-1];
if (f[i] >= mod) f[i] -= mod;
}
for (int i = 1; i <= n; i++) {
f[i] += f[i-1];
if (f[i] >= mod) f[i] -= mod;
}
}
int main() {
init(1e6);
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
printf("%d\n", f[n]);
}
return 0;
}
hdu 5072 Coprime
傳送門
題意:給定n個不一樣的數,求多少個a,b,c知足兩兩互質或者兩兩不互質。
分析:千萬別當作gcd相等了。。。明顯地,從正面考慮是不太現實的,狀況數及其多,咱們考慮從反面出發,C(n,3)減去不合法的數目便可。
若是一個三元組abc不合法,那麼一定存在一個數與其中一個數互質,而與另一個數不互質,那麼咱們枚舉每個數a[i],算有多少個數與它互質,有多少個數與它不互質,假設有s個數與a[i]互質,那麼a[i]對答案的貢獻是s*(n-1-s),注意到這樣計算會有重複,好比說7 15 10符合條件,此時7與10互質,可是7 10 15一樣符合條件,若是7 15 21符合條件,此時7與21不互質可是15 7 21一樣符合條件,因此最後須要除以2去除重複的狀況。
算互質的個數能夠經過容斥作,以前也有提到過即ans =
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int num[N], miu[N], pri[N], cnt[N], a[N];
bool vis[N];
void init() {
int k = 0;
miu[1] = 1;
for (int i = 2; i <= 100000; i++) {
if (!vis[i]) pri[k++] = i, miu[i] = -1;
for (int j = 0; j < k; j++) {
int s = i * pri[j];
if (s > 100000) break;
vis[s] = 1;
if (i % pri[j] == 0) break;
miu[s] = -miu[i];
}
}
}
int main() {
init();
int t, ca = 0;
scanf("%d", &t);
while (t--) {
int n, ma = 0;
scanf("%d", &n);
memset(vis, 0, sizeof(vis));
for (int i = 0; i < n; i++) {
scanf("%d", a + i); vis[a[i]] = 1;
ma = max(ma, a[i]);
}
for (int i = 1; i <= ma; i++) if (miu[i]) {
for (int j = i; j <= ma; j += i) if (vis[j]) num[i]++;
num[i] *= miu[i];
for (int j = i; j <= ma; j += i) cnt[j] += num[i];
}
cnt[1]--;
LL ans = (LL)n * (n - 1) * (n - 2) / 6, s = 0;
for (int i = 0; i < n; i++) s += (LL)cnt[a[i]] * (n - 1 - cnt[a[i]]);
printf("%lld\n", ans - s / 2);
if (t) {
memset(num + 1, 0, ma << 2);
memset(cnt + 1, 0, ma << 2);
}
}
return 0;
}
hdu 6102 GCDispower
傳送門
題意:給定1~n(n <= 100000)的一個排列,定義區間[L, R]的power值爲
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
struct po{
int f, s;
po(int f = 1, int s = 1) : f(f), s(s) {}
};
void read(int& res) {
char c;
while (!(isdigit(c = getchar())));
res = c - '0';
while (isdigit(c = getchar())) res = res * 10 + c - '0';
}
vector<po> q[N], d[N];
vector<int> f[N];
LL c[N], ans[N];
int pos[N], v[N], n, m, a[N], cnt[N];
int lowbit(int x) {
return x & -x;
}
LL get(int x) {
LL res = 0;
while (x) { res += c[x]; x -= lowbit(x); }
return res;
}
void add(int x, LL va) {
int t = x;
while (x <= n) { c[x] += va; x += lowbit(x); }
}
int getnum(int x) {
int res = 0;
for (int i = 0; i < d[x].size(); i++) res += cnt[d[x][i].f] * d[x][i].s;
return res;
}
void upd(int x, bool v) {
for (int i = 0; i < d[x].size(); i++) v ? ++cnt[d[x][i].f] : --cnt[d[x][i].f];
}
void init(int n) {
for (int i = 2; i <= n; i++) if (!v[i]) {
for (int j = i; j <= n; j += i) {
v[j] = 1;
f[j].push_back(i);
}
}
for (int i = 2; i <= n; i++) {
int s = 1 << f[i].size();
for (int j = 0; j < s; j++) {
d[i].push_back(po());
for (int k = 0; k < f[i].size(); k++) {
if (j >> k & 1) d[i][j].f *= f[i][k], d[i][j].s *= -1;
}
}
}
}
void solve() {
for (int i = 1; i <= n; i++) {
int k = 1;
for (int j = a[i] << 1; j <= n; j += a[i]) {
if (pos[j] < i) v[k++] = pos[j];
}
sort(v + 1, v + k);
LL sum = 0;
for (int j = k - 1; j >= 1; j--) {
int va = a[v[j]] / a[i];
sum += (LL)getnum(va) * a[i];
add(v[j-1] + 1, sum); add(v[j] + 1, -sum);
upd(va, 1);
}
for (int j = k - 1; j >= 1; j--) upd(a[v[j]] / a[i], 0);
for (po j : q[i]) ans[j.f] = get(j.s);
}
}
int main() {
init(100000);
int t;
read(t);
while (t--) {
read(n); read(m);
for (int i = 1; i <= n; i++) {
read(a[i]); c[i] = 0;
q[i].clear(); pos[a[i]] = i;
}
for (int i = 1; i <= m; i++) {
int l, r;
read(l); read(r);
q[r].push_back(po(i, l));
}
solve();
for (int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
}
return 0;
}
hdu 6134 Battlestation Operational
傳送門
題意:計算
分析:這是一個明顯的前綴和形式,計算
到了這裏,就可以看出來明顯的套路了。線性篩因子個數,而後對於每個數直接枚舉其倍數算貢獻,最後統計前綴和便可。查詢O(1),複雜度
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 5, mod = 1e9 + 7;
bool vis[N];
int prime[N], phi[N], g[N];
short miu[N];
LL sum[N], f[N];
void init(int n) {
phi[1] = sum[1] = miu[1] = 1;
int k = 0;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
prime[k++] = i;
g[i] = 1; phi[i] = i - 1;
sum[i] = 2; miu[i] = -1;
}
for (int j = 0; j < k; j++) {
int s = i * prime[j];
if (s > n) break;
vis[s] = 1;
if (i % prime[j] == 0) {
g[s] = g[i] + 1;
sum[s] = sum[i] + sum[i] / g[s];
phi[s] = phi[i] * prime[j];
break;
}
g[s] = 1; sum[s] = sum[i] << 1;
phi[s] = phi[i] * (prime[j] - 1);
miu[s] = -miu[i];
}
}
for (int i = 2; i <= n; i++) sum[i] += sum[i-1];
for (int i = 1; i <= n; i++) {
if (miu[i]) for (int j = i; j <= n; j += i) f[j] = (f[j] + miu[i] * sum[j/i]) % mod;
if (f[i] < 0) f[i] += mod;
}
for (int i = 1; i <= n; i++) f[i] = (f[i] + phi[i] - 1 + f[i-1]) % mod;
}
int main() {
init(1e6);
int n;
while (~scanf("%d", &n)) printf("%lld\n", f[n]);
return 0;
}
接下來來看一些積性函數的問題。
hdu 5628 Clarke and math
傳送門
題意:給定f(i)
分析: 這裏咱們能夠看出來
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5, mod = 1e9 + 7;
LL ans[N], f[N], g[N], h[N];
int n, k;
void calc(LL* f, LL* g) {
memset(h + 1, 0, n << 3);
for (int i = 1; i * i <= n; i++) {
h[i * i] += f[i] * g[i] % mod;
if (h[i * i] >= mod) h[i * i] -= mod;
for (int j = i + 1; i * j <= n; j++) {
h[i * j] += f[i] * g[j] % mod + f[j] * g[i] % mod;
while (h[i * j] >= mod) h[i * j] -= mod;
}
}
copy(h + 1, h + 1 + n, g + 1);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; i++) scanf("%lld", f + i), ans[i] = 0, g[i] = 1;
ans[1] = 1;
while (k) {
if (k & 1) calc(g, ans);
calc(g, g); k >>= 1;
}
calc(f, ans);
for (int i = 1; i < n; i++) printf("%lld ", ans[i]);
printf("%lld\n", ans[n]);
}
return 0;
}
hdu 5970 最大公約數
傳送門
題意:定義函數f以下:
void f(int x, int y) {
int c = 0;
while (y) {
c++;
int t = x % y;
x = y;
y = t;
}
}
return c * x * x;
給定
分析:m的範圍很小,這個一定是關鍵點。
分析f函數能夠知道,對於
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 670;
int c[N][N], gcd[N][N];
void init(int n) {
gcd[1][1] = c[1][1] = 1;
for (int i = 2; i <= n; i++) {
gcd[i][i] = i; c[1][i] = 2;
gcd[i][1] = gcd[1][i] = c[i][i] = c[i][1] = 1;
for (int j = 2; j < i; j++) {
if (!gcd[i][j]) gcd[i][j] = gcd[j][i-j];
gcd[j][i] = gcd[i][j];
c[i][j] = c[j][i % j] + 1;
c[j][i] = c[i][j] + 1;
}
}
}
int main() {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
init(666);
int t;
scanf("%d", &t);
while (t--) {
int n, m, p;
scanf("%d %d %d", &n, &m, &p);
int ans = 0;
for (int j = 1; j <= m; j++) {
for (int i = 1; i <= j && i <= n; i++) {
int _c = c[i][j], g = gcd[i][j] * gcd[i][j];
for (int k = 0; k < _c; k++) {
if (i + k * j > n) break;
LL tm = (n - (i + j * k)) / (_c * j) + 1;
ans += (LL)(i + j * k) * j / (_c * g) * tm % p;
if (ans >= p) ans -= p;
ans += (tm - 1) * tm / 2 % p * (j * j / g) % p;
if (ans >= p) ans -= p;
}
}
}
printf("%d\n", ans);
}
return 0;
}
hdu 5528 Count a * b
傳送門
題意:
分析:求i*j % mod m != 0不太方便,咱們從反面考慮轉化爲熟悉的問題。
那麼答案就是ans =
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long LL;
const int N = 31630;
bool vis[N];
int prime[N], k;
void init(int n) {
for (int i = 2; i <= n; i++) {
if (!vis[i]) prime[k++] = i;
for (int j = 0; j < k; j++) {
int s = i * prime[j];
if (s > n) break;
vis[s] = true;
if (i % prime[j] == 0) break;
}
}
prime[k] = 40000;
}
LL solve(int n) {
int m = n;
LL all = 1, sum = 1;
for (int i = 0; prime[i] * prime[i] <= n; i++) {
int p = prime[i];
if (n % p == 0) {
int cnt = 0;
while (n % p == 0) n /= p, cnt++;
LL f = 1, s = 1;
for (int j = 0; j < cnt; j++) s *= p, f += s * s;
all *= f;
sum *= (cnt + 1);
}
}
if (n != 1) all *= (LL)n * n + 1, sum <<= 1;
return all - m * sum;
}
int main() {
//freopen("in.txt", "r", stdin);
//freopen("out1.txt", "w", stdout);
init(31625);
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
printf("%llu\n", solve(n));
}
return 0;
}
看到上面那題,忍不住要把一個類似的題拿出來-_-。
csu 1934 計數
傳送門
題意:給定n,m,a,求知足x*y%a==0且1<=x<=n,1<=y<=m的(x,y)的對數,n<=1e18,m<=1e18,a<=1e9。
分析:彷佛看起來與上題有點類似,可是注意到這裏x和y是能夠大於a的,因此沒辦法搞,那麼咱們從通常的思路考慮。咱們只須要枚舉a的因子,而後直接算便可,也就是說ans =
對a作質因數分解,假設
彷佛說的一點也不清楚?
#include <cstdio>
using namespace std;
typedef long long LL;
const LL mod = 333333333333333331LL;
int pri[31625], fac[13][2], k, s[13], p[13][30], lef[15], val[15];
bool vis[31625];
LL a, b, c, ans, t;
LL mulmod(LL x, LL y) {
if (x >= mod) x %= mod;
if (y >= mod) y %= mod;
LL tmp = (x * y - (LL)((long double)x / mod * y + 1e-8) * mod) % mod;
return tmp < 0 ? tmp + mod : tmp;
}
void init(int n) {
for (int i = 0; i <= 11; i++) lef[i] = 1 << i;
for (int i = 2; i <= n; i++) if (!vis[i]) {
pri[k++] = i;
for (int j = i * i; j <= n; j += i) vis[j] = 1;
} pri[k++] = 31650;
}
void dfs(int i, LL mul, int* s) {
if (i == k) {
int l = 0;
for (int j = 0; j < k; j++) if (s[j] < fac[j][1]) val[l++] = fac[j][0];
LL sum = b / mul;
for (int j = 1; j < lef[l]; j++) {
LL tmp = mul; bool f = 0;
for (int _i = 0; _i < l; _i++) if (j & lef[_i]) f = !f, tmp *= val[_i];
sum += f ? -(b / tmp) : b / tmp;
}
ans += mulmod(sum, c / (t / mul));
if (ans >= mod) ans -= mod;
return ;
}
for (int j = 0; j <= fac[i][1]; j++) {
s[i] = j;
dfs(i + 1, mul * p[i][j], s);
}
}
int main() {
init(31623);
while (~scanf("%lld %lld %lld", &a, &b, &c)) {
if (b < a / c) { puts("0"); continue; }
ans = k = 0; t = a;
for (int i = 0, j = pri[0]; a != 1 && j * j <= t; j = pri[++i]) {
if (a % j == 0) {
int cnt = 0; p[k][0] = 1;
while (a % j == 0) a /= j, ++cnt, p[k][cnt] = p[k][cnt-1] * j;
fac[k][0] = j, fac[k++][1] = cnt;
}
}
if (a > 1) p[k][1] = fac[k][0] = a, p[k][0] = fac[k++][1] = 1;
dfs(0, 1, s);
printf("%lld\n", ans);
}
return 0;
}
51nod 1244 莫比烏斯函數之和
傳送門
題意:求
分析:令
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7 + 5;
unordered_map<LL, int> mp;
bool vis[N];
int prime[N], miu[N];
void init(int n) {
miu[1] = 1;
int k = 0;
for (int i = 2; i <= n; i++) {
if (!vis[i]) prime[k++] = i, miu[i] = -1;
for (int j = 0; j < k; j++) {
int s = i * prime[j];
if (s > n) break;
vis[s] = 1;
if (i % prime[j] == 0) { break; }
else miu[s] = -miu[i];
}
}
for (int i = 2; i <= n; i++) miu[i] += miu[i-1];
}
LL d(LL n) {
if (n <= 10000000LL) return miu[n];
if (mp[n]) return mp[n];
int res = 1;
for (LL i = 2, last; i <= n; i = last + 1) {
last = n / (n / i);
res -= (last - i + 1) * d(n / i);
}
return mp[n] = res;
}
int main() {
init(1e7);
LL a, b;
while (~scanf("%lld %lld", &a, &b)) printf("%d\n", d(b) - d(a - 1));
return 0;
}
51nod 1244 歐拉函數之和
傳送門
題意:求
分析:令
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e9 + 7, inv = 500000004LL;
const int N = 1e7 + 5;
unordered_map<LL, LL> mp;
bool vis[N];
int prime[N], phi[N];
void init(int n) {
phi[1] = 1;
int k = 0;
for (int i = 2; i <= n; i++) {
if (!vis[i]) prime[k++] = i, phi[i] = i - 1;
for (int j = 0; j < k; j++) {
int s = i * prime[j];
if (s > n) break;
vis[s] = 1;
if (i % prime[j] == 0) {
phi[s] = phi[i] * prime[j];
break;
}
else phi[s] = phi[i] * (prime[j] - 1);
}
}
for (int i = 2; i <= n; i++) phi[i] = (phi[i] + phi[i-1]) % mod;
}
LL d(LL n) {
if (n <= 10000000LL) return phi[n];
if (mp[n]) return mp[n];
LL res = (n % mod * ((n + 1) % mod)) % mod * inv % mod;
for (LL i = 2, last; i <= n; i = last + 1) {
last = n / (n / i);
res = (res - (last - i + 1) * d(n / i) % mod + mod) % mod;
}
return mp[n] = res;
}
int main() {
init(1e7);
LL n;
while (~scanf("%lld", &n)) printf("%lld\n", d(n));
return 0;
}
hdu 5608 function
傳送門
題意:求
分析:最基礎的杜教篩,和上面兩題同樣,拿來構造狄利克雷卷積的函數g(n)都是恆等函數I(n)=1。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e9 + 7, inv1 = 166666668LL, inv2 = 500000004LL;
const int N = 1e6 + 5;
LL f[N];
unordered_map<LL, LL> mp;
void init(int n) {
f[1] = 0;
for (int i = 2; i <= n; i++) {
f[i] = ((LL)i * i % mod - 3 * i + mod) % mod + 2;
if (f[i] >= mod) f[i] -= mod;
}
int up = n >> 1;
for (int i = 2; i <= up; i++) {
for (int j = i << 1; j <= n; j += i) f[j] = (f[j] - f[i] + mod) % mod;
}
for (int i = 2; i <= n; i++) f[i] = (f[i] + f[i-1]) % mod;
}
LL get(LL n) {
if (n <= 1000000LL) return f[n];
if (mp.count(n)) return mp[n];
LL res = n * (n + 1) % mod * (n << 1 | 1) % mod * inv1 % mod;
res = (res - 3 * (n * (n + 1) % mod * inv2 % mod) % mod + mod) % mod;
res = (res + 2 * n) % mod;
for (LL i = 2, last; i <= n; i = last + 1) {
last = n / (n / i);
res = (res - (last - i + 1) * get(n / i) % mod + mod) % mod;
}
return mp[n] = res;
}
int main() {
init(1e6);
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
printf("%lld\n", get(n));
}
return 0;
}
51nod 1238 最小公倍數之和
傳送門
題意:求
分析:ans =
令
能夠知道
51nod 1238 最大公約數之和
傳送門
題意:求
分析:相比上題,本題容易多了。。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e9 + 7, inv = 500000004LL;
const int N = 1e7 + 5;
unordered_map<LL, LL> mp;
bool vis[N];
int prime[N], phi[N];
void init(int n) {
phi[1] = 1;
int k = 0;
for (int i = 2; i <= n; i++) {
if (!vis[i]) prime[k++] = i, phi[i] = i - 1;
for (int j = 0; j < k; j++) {
int s = i * prime[j];
if (s > n) break;
vis[s] = 1;
if (i % prime[j] == 0) {
phi[s] = phi[i] * prime[j];
break;
}
else phi[s] = phi[i] * (prime[j] - 1);
}
}
for (int i = 2; i <= n; i++) phi[i] = (phi[i] + phi[i-1]) % mod;
}
LL d(LL n) {
if (n <= 10000000LL) return phi[n];
if (mp.count(n)) return mp[n];
LL res = (n % mod * ((n + 1) % mod)) % mod * inv % mod;
for (LL i = 2, last; i <= n; i = last + 1) {
last = n / (n / i);
res = (res - (last - i + 1) * d(n / i) % mod + mod) % mod;
}
return mp[n] = res;
}
int main() {
init(1e7);
LL n;
scanf("%lld", &n);
LL res = 0;
for (LL i = 1, last; i <= n; i = last + 1) {
LL x = n / i; last = n / x; x %= mod; x = x * x % mod;
res = (res + (d(last) - d(i - 1) + mod) % mod * x % mod) % mod;
}
printf("%lld\n", res);
return 0;
}
51nod 1227 平均最小公倍數
傳送門
題意:定義
分析:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e9 + 7, inv = 500000004LL, inv1 = 166666668LL;
const int N = 3876556;
unordered_map<LL, LL> mp;
bool vis[N];
LL prime[N], phi[N];
void init(LL n) {
phi[1] = 1;
LL k = 0;
for (LL i = 2; i <= n; i++) {
if (!vis[i]) prime[k++] = i, phi[i] = i - 1;
for (LL j = 0; j < k; j++) {
LL s = i * prime[j];
if (s > n) break;
vis[s] = 1;
if (i % prime[j] == 0) {
phi[s] = phi[i] * prime[j];
break;
}
phi[s] = phi[i] * (prime[j] - 1);
}
}
for (LL i = 2; i <= n; i++) phi[i] = (i * phi[i] + phi[i-1]) % mod;
}
LL p1(LL L, LL R) {
return ((L + R) * (R - L + 1) >> 1) % mod;
}
LL d(LL n) {
if (n <= 3876550LL) return phi[n];
if (mp.count(n)) return mp[n];
LL res = n * (n + 1) % mod * ((n << 1 | 1) % mod) % mod * inv1 % mod;
for (LL i = 2, last; i <= n; i = last + 1) {
last = n / (n / i);
res -= p1(i, last) * d(n / i) % mod;
if (res < 0) res += mod;
}
return mp[n] = res;
}
LL solve(LL n) {
LL res = d(n);
for (LL i = 2, last; i <= n; i = last + 1) {
last = n / (n / i);
res += (last - i + 1) * d(n / i) % mod;
if (res >= mod) res -= mod;
}
return (res + n) * inv % mod;
}
int main() {
init(3876550);
LL a, b;
scanf("%lld %lld", &a, &b);
printf("%lld\n", (solve(b) - solve(a - 1) + mod) % mod);
return 0;
}
51nod 1222 最小公倍數計數
傳送門
題意:給定a,b,求有多少個元組(x,y)知足,