莫比烏斯與積性函數

莫比烏斯與積性函數

以前作過很多的數論題,關於莫比烏斯與積性函數的數論題挺多的。。。特意過來總結一下。。看成本身的一個回顧了-_-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

u(i)=1,(1)k,0,if n = 1if n=p1p2...pk 

根據上面的定義,n大於1時且n是平方因子數時莫比烏斯函數值爲0,不然從n的惟一分解定理中根據素數的個數取奇偶便可。

莫比烏斯函數的性質:數組

1) d|nu(d)=1,0,if n = 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) d|nu(d)d=φ(n)n 函數

莫比烏斯函數除了能夠用在莫比烏斯反演中以外,還能夠用來進行容斥。
舉一個常見的例子,求取1到n這個區間內有多少個數與x互質,通常的作法是直接進行容斥,可是咱們能夠發現容斥的係數恰好是莫比烏斯函數,即ans = d|xu(d)nd ,其實這二者從本質考慮是徹底等價的。工具

莫比烏斯反演

莫比烏斯反演是一個這樣的式子:
定義 F(n)=d|nf(n) ,那麼能夠獲得 f(n)=d|nu(nd)F(d)
莫比烏斯反演還有一種更常見的形式: F(n)=n|df(d) , 那麼有 f(n)=n|du(dn)F(d)
通常應用的都是上述的第二種形式,證實能夠經過概括得出,也能夠直接經過式子變換得出,還能夠由狄利克雷卷積證實。
f(n)=d|nu(nd)F(d)=d|nu(nd)x|df(x)=d|nf(d)x|ndu(x)=f(n)
上述的證實中給出了一種常見的和式變換技巧:交換求和順序。經過交換求和順序,咱們每每能夠將某些和式化簡或者更容易求出該和式,後面公式的化簡將屢次用到。學習

莫比烏斯反演還有一個推廣式,以下:
這裏寫圖片描述

積性函數

積性函數定義

f(x) 爲一個數論函數,若是對於任意正整數a、b知足 (a,b)=1 ,有 f(ab)=f(a)f(b) 的話,咱們稱 f(n) 爲積性函數;若是對於任意正整數啊a、b有 f(ab)=f(a)f(b) 的話,咱們稱 f(n) 爲徹底積性函數。
常見的積性函數有:
因子個數函數 d(n) ,因子和函數 σ(n) ,二元函數 gcd(a,b) ,歐拉函數 φ(n) ,莫比烏斯函數 u(n)
徹底積性函數有:
元函數 e(n)=[n==1] ,恆等函數 I(n)=1 ,單位函數 id(n)=n

積性函數應用

咱們來看看積性函數的應用:
若是 f(x) 爲積性函數且 n=ti=1peii ,那麼能夠獲得 f(n)=ti=1f(peii) ,若是f(x)爲徹底積性函數,那麼咱們還能夠進一步獲得 f(n)=ti=1f(pi)ei
舉個簡單的例子:因子個數 d(n)=ti=1d(peii)=ti=1(ei+1) ,因子和函數 σ(n)=ti=1σ(peii)=ti=1piei+11pi1
積性函數還能夠用來進行線性篩從而獲得不少數論函數的值~
好比下面的歐拉函數:
能夠知道當 i%p==0 時, φ(ip)=φ(i)p ,而當 i%p!=0 時,有

φ(ip)=φ(i)φ(p)=φ(i)(p1)

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,知足 (fg)(n)=d|nf(d)g(nd)
狄利克雷卷積知足如下定律:
這裏寫圖片描述
若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];
    }
}
舉例

不少數論函數均可以用狄利克雷卷積來表示:
①約數個數函數 d(n)=(II)(n) ;
②約數和函數 d(n)=(Iid)(n) ;
d|nu(d)=[n==1] ,獲得 Iu=e
d|nφ(d)=n ,獲得 Iφ=id

如今咱們能夠經過狄利克雷卷積來證實莫比烏斯反演了:
若是 F(n)=d|nf(n) ,那麼F = I*f,那麼能夠知道F*u = I*f*u=f*(I*u)=f*e=f,因此 f(n)=d|nu(nd)F(d)

應用

設f(n)爲一個數論函數,須要計算 S(n)=ni=1f(i) ,一般n>=1e9。
要解決上述問題,咱們能夠構造一個 S(n) 關於 S(ni) 的遞推式。
找到一個合適的數論函數g(n),

i=1nd|if(d)g(id)=i=1ng(i)j=1nif(j)=i=1ng(i)S(ni)

這樣,能夠知道 g(1)S(n)=ni=1(fg)(i)ni=2g(i)S(ni)
構造的卷積函數必須容易求得前綴和,這樣咱們能夠記憶化地計算S(n),由於 ni 是分段的,能夠計算出複雜度爲 O(n34) ,只要咱們預處理S函數前 23 的值,複雜度就能夠達到 O(n23)
上述的辦法就是傳說中的杜教篩了~
若是找不到適合的函數g(n),那麼能夠採用一種複雜度爲 O(n34logn) 的辦法來實現求和,詳見任之州的論文。

下面給出求積性函數前綴和時經常使用到的一些公式和結論:
d|nu(d)=[n==1]
d|nφ(d)=n
ni=1i[(i,n)==1]=nφ(n)+[n==1]2
d(n2)=d|n2w(d)=d|ni|du2(i) ,w(d)表示d的不一樣質因子的個數。
d(nm)=i|nj|m[(i,j)==1]

i=1n[(i,n)==1](i1,n)=d|ni=1n[(i,n)==1][d|(i1)]=d|nφ(d)φ(n)φ(d)=d(n)φ(n)

d|ijd(i,d)|j
上面的幾個東西有些是很顯然的,可是仍是寫出來放在一塊兒好了,至於第六個本人能力過弱不知如何證實。。。

習題

bzoj 2420 徹底平方數
傳送門
題意:求第n個無平方因子數。
分析:問題等價於求最小的x使得1到x內有n個無平方因子數,咱們考慮容斥,總數減掉2^2的倍數個數,3^2的倍數個數,5^2的倍數個數….加上(2*3)^2的倍數個數,加上(3*5)^2的倍數個數,加上(3*5)^2的倍數個數…..能夠發現容斥的係數就是莫比烏斯函數。這樣咱們二分x,而後 x 求個數便可。
不過注意到m/(i*i)的值是分塊的,因此咱們能夠分塊求,因此複雜度下界大概是 x

#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了。

f(n,m)=i=1nj=1m[(i,j)==1]=i=1nj=1md|(i,j)u(d)=i=1min(n,m)u(d)ndmd
這樣咱們能夠直接分塊, ndmd2(n+m) 個取值,單次查詢複雜度爲 O(n)
利用莫比烏斯反演咱們一樣能夠獲得上述的式子。

#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)有多少對。
分析:按照上題的套路,咱們知道

f(n,m)=pd=1min(np,mp)u(d)ndpmdp
。直接枚舉質數而後 O(n) 求解複雜度仍是有點高,咱們考慮繼續化簡式子。
f(n,m)=pd=1min(np,mp)u(d)ndpmdp=x=1min(n,m)nxmxp|xu(xp)
,只要對 p|du(dp) 求一個前綴和,這個能夠經過對每一個質數,枚舉其倍數完成,而質數只有 O(nlogn) 個, 以後就能夠 O(n) 求解。
這裏用到了一個結論: nxy=nxy 。交換求和順序化簡和式,分析複雜度都是解決本題的關鍵。

#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,求 ni=1mj=1F((i,j))[(i,j)<=a] ,F(i)表示i的約數和。
分析:出題人相比上面幾題加了一點套路而已-_-。
對於這種帶有限制的計數問題,若是很差直接解決,咱們能夠先直接分析,而後在考慮限制,後面每每能夠直接算知足限制的,或者容斥,固然這裏與容斥無關。。
先無論a,令ans = \sum_{i=1}^{n}\sum_{j=1}^{m}F((i, j))=\sum_{g=1}^{min(n,m)}F(g) * num(g) ni=1mj=1F((i,j))=min(n,m)g=1F(g)num(g) ,這裏num(g)表示有多少對(i,j)使得(i,j)==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}

ans=i=1nj=1mF((i,j))=g=1min(n,m)F(g)i=1min(ng,mg)u(i)ngimgi

= \sum_{T=1}^{min(n,m)}\frac{n}{T}*\frac{m}{T} * \sum_{g|T}F(g)*u(\frac{T}{g})
=T=1min(n,m)nTmTg|TF(g)u(Tg)

同上題,線性篩處理F函數、莫比烏斯函數以及 \sum_{g|T}F(g)*u(\frac{T}{g}) g|TF(g)u(Tg) 的前綴和,就能夠 O(\sqrt n) O(n) 分塊搞了。
如今考慮a的限制,咱們計算時只能考慮(i,j)<=a的貢獻,因此咱們能夠將F(i)從小到大排序,將a從小到大排序,這樣子每次咱們只須要對小於等於a的全部F值更新其倍數,因此須要單點修改而且在分塊時求前綴和,那麼能夠考慮使用樹狀數組維護。
取模是對2的冪取模的,可使用int計算而後直接相與便可。
本題也算是本蒟蒻作過的爲數很少的樹狀數組了,以爲樹狀數組在一些帶限制的且須要對一個範圍內的權值統計和時應用挺多的,單點修改和統計前綴和都是 O(logn) 的。

#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
傳送門
題意:多組詢問,求 ni=1mj=1lcm(i,j)
分析:先將lcm轉化爲熟悉的gcd,咱們有

ans=i=1nj=1mij(i,j)=g=1min(n,m)gF(g)

此處F(g)表明因此有(i,j)==g的i*j之和。那麼咱們有
F(g)=d=1min(ng,mg)u(d)ddi=1ngdj=1mgdij

sum(x,y)=xi=1yj=1ij ,那麼有
ans=g=1min(n,m)gd=1min(ng,mg)u(d)d2sum(ngd,mgd)=T=1min(n,m)sum(nT,mT)g|TTgu(g)g2

一樣的套路,咱們只須要求出 f(T)=g|TTgu(g)g2 的前綴和,那麼就能夠 O(n) 計算了。
f(T)是積性函數,能夠用線性篩求f(T),當T%p==0,原來T的因子部分變爲原來的p倍了,多增長的因子都是有平方因子數,這個都爲0,不用考慮。
因此有
f(Tp)={pf(T),f(T)(pp2),if T%p==0;

ps:這個題目的mod不要一眼當作1e9+7了,否則錯都不知道在哪。。。

#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知足 1BiAi ,且gcd(B_1,B_2,...B_n)>2 gcd(B1,B2,...Bn)>2
分析:相信作完上面的題目以後很容易分析得出ans = \sum_{T=1}^{min(A_1,A_2,...A_n)}-u(T)*\prod_{i=1}^{n}\frac{A_i}{T} ans=min(A1,A2,...An)T=1u(T)ni=1AiT 。多校時這題作到這就不知道怎麼優化計算了。。而後各類優化仍是超時。。。全世界都會優化的題我不會。。或者這就是弱渣吧。
此處分塊沒任何意義,咱們考慮暴力枚舉T,快速計算\prod_{i=1}^{n}\frac{A_i}{T} ni=1AiT ,注意到數據的範圍不大,咱們能夠分段計算,在kT到(k+1)T-1這一段的數除以T都爲k,這樣子咱們只須要統計出cnt[]數組的前綴和,對於每個T,暴力枚舉其倍數的分段,而後快速冪計算便可。總複雜度O(nlog^2n) O(nlog2n)
另外咱們也能夠經過容斥原理獲得上述的式子,此處只須要考慮無平方因子數便可,咱們加上以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)=ni=1nj=1[lcm(i,j)+gcd(i,j)>=n] ,求S(n)=\sum_{i=1}^{n}F(i) S(n)=ni=1F(i)
分析:考慮F(n) F(n) ,這個大於等於不太好考慮,咱們不妨看看等於的狀況。
f(n)=\sum_{i=1}^{n}\sum_{j=1}^{n}[lcm(i,j)+gcd(i,j)==n] f(n)=ni=1nj=1[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)}

f(n)=i=1nj=1n[ijgcd(i,j)+gcd(i,j)==n]=g=1ni=1ngj=1ng[(i,j)==1][g(ij+1)==n]=g|ni=1ngj=1ng[ij==ng1][(i,j)==1]=g|n2G(ng1)
,這裏G(n)表示n的不一樣的質因子的個數。
因此,咱們能夠線性篩 G(n) G(n) ,而後 O(nlogn) O(nlogn) 更新 f(n) f(n)
既然已經知道了 f(n) f(n) ,那麼咱們能夠考慮容斥計算 F(n) F(n) ,有
F(n)=i=1nj=1n[lcm(i,j)+gcd(i,j)>=n]=
i=1n1j=1n1[lcm(i,j)+gcd(i,j)>=n1]+(2n1)f(n1)

那麼能夠獲得F的遞推式, F(n)=F(n1)f(n1)+2n1
預處理F以及S便可。

#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 = d|xu(d)num(d) ,num(d)表示d的倍數的個數,因此咱們只須要O(nlogn)算倍數個數,O(nlogn)更新,O(n)求解不合法的數目便可。

#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值爲

i=LRj=i+1Rk=j+1R(gcd(p[i],p[j])==p[k])p[k]
,有m(m<=100000)個查詢,每次給出L,R,求該區間的poewr值。
分析:本題應該是屬於比較好的題了!問題等價於求解
k=L+2Rp[k]i=Lk2j=i+1k1(gcd(p[i],p[j])==p[k])
,因此說對於每個k,咱們須要求解在區間內有多少組(i,j)使得 gcd(p[i],p[j])==p[k] ,那麼咱們首先能夠知道能產生貢獻的一定是位置在k左邊的倍數對,並且對於每個倍數p[i],它能貢獻左端點在1到i之間的區間,產生的貢獻是i右邊與p[i]最大公約數爲p[k]的對的數量,這個能夠在素因子的組合之間作容斥求出。因此咱們能夠從右往左枚舉每個倍數,而且更新該位置產生的貢獻,這個可使用樹狀數組維護。更新的時候選取的老是最左邊的那個知足要求的位置,注意到是求前綴和,因此更新地時候還要考慮到這個問題,只須要在後面這樣,查詢時是 O(logn) 的。
預處理一個數的全部素因子組合,而後從右往左枚舉每個數的倍數,而後維護和更新產生的貢獻。考慮到查詢數比較多,所有離線處理一次,複雜度是 O(nlog2n) 的,單組查詢 O(1)
ps:本題值得好好總結,感受方法很好。

#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
傳送門
題意:計算 f(n)=ni=1ij=1ij[(i,j)==1]
分析:這是一個明顯的前綴和形式,計算 g(i)=ij=1ij[(i,j)==1]
g(i)=φ(i)1+ij=1ijd|(i,j)u(d)=φ(i)1+d|iu(d)idj=1ijd
到了這裏,就可以看出來明顯的套路了。線性篩因子個數,而後對於每個數直接枚舉其倍數算貢獻,最後統計前綴和便可。查詢O(1),複雜度 O(nlogn)

#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) f(i) 序列, 1<=i<=n ,求 g(i)=i1|ii2|i1i3|i2...ik|ik1f(ik) , 1<=i<=n
分析: 這裏咱們能夠看出來 g 函數是 f 函數與k個恆等函數 I 的狄利克雷卷積,而狄利克雷卷積知足交換律和結合律,因此咱們能夠知道 g(n)=(fIk)(n) ,這樣咱們只須要快速冪計算k個恆等函數的卷積,而後最後在作一次與f函數的卷積就夠了,複雜度 O(nlognlogk) 。初始化爲元函數 e(n) 便可。

#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;
給定 n<=666666666,m<=666,p<=666666666 ,求 ni=1mj=1ijf(i,j)
分析:m的範圍很小,這個一定是關鍵點。
分析f函數能夠知道,對於 x=i(mody) 的全部x, f(x,y) 函數返回的值都是相同的。咱們能夠獲得

ans=i=1mj=1ik=0j+ik<=ni(j+ik)(i,j)(i,j)c(j,i)

到了這裏,彷佛沒辦法直接搞,可是考慮到k的範圍能夠進一步根據c(i,j)來縮小。
那麼咱們獲得
ans=i=1mj=1ik=0c(i,j)1l=0j+i(k+c(i,j)l)<=ni(j+i(k+c(i,j)l))(i,j)(i,j)c(i,j)=i=1mj=1ik=0c(i,j)1l=0j+i(k+c(i,j)l)<=n(i(j+ik)(i,j)(i,j)c(i,j)+lii(i,j)(i,j))

到了這裏,咱們只須要枚舉 i,j,k ,而後計算等差數列的和了。複雜度 O(m2log(m))

#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
傳送門
題意: f(m)=m1i=0m1j=0[ijmodm!=0] ,求 g(n)=m|nf(m)
分析:求i*j % mod m != 0不太方便,咱們從反面考慮轉化爲熟悉的問題。
那麼答案就是ans = m|nm2m1i=0m1j=0[ijmodm==0] ,這裏咱們令 f(m)=m1i=0m1j=0[ijmodm==0] ,那麼

f(m)=i=0m1mm(i,m)=d|mdφ(md)
.
因此
m|nd|mdφ(md)=d|ndk|ndφ(k)=d|ndnd=d(n)n

因子個數是積性函數,那麼如今令 F(n)=d|nd2 。很容易能夠看出這一個積性函數,那麼咱們只須要算 F(pe)=1+p2+p4+...+p2e=p2e+21p21
到了這裏,咱們只須要對n作質因數分解,而後直接算便可。對 264 的取模能夠用unsigned long long。咱們能夠預處理一下素數,這樣子質因數分解的複雜度就是最多 O(nlogn) 的。

#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 = d|andmda 。注意到這裏明顯是有重複的,因此咱們考慮容斥。
對a作質因數分解,假設 a=ti=1peii 。算出 x=pf11pf22...pftt(0<=fi<=ei) 的每一種形式下知足條件的對數,這樣子就不會有重複了,在算總共 ti=1(ei+1) 每種x下時的數目時若是x關於a的某個質因數 pi 的指數 fi 小於 ei ,那麼這時咱們就須要容斥減掉這個指數爲 fi+1 的狀況了~。
彷佛說的一點也不清楚?

#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 莫比烏斯函數之和
傳送門

題意:求 bi=au(i)
分析:令 s(n)=ni=1u(i) ,那麼

s(n)=i=1n([i==1]d|i,d<iu(d))=1j=2ns(nj)
.直接杜教篩便可。

#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 歐拉函數之和
傳送門
題意:求 ni=1φ(i)
分析:令 s(n)=ni=1φ(i) ,那麼

s(n)=i=1n(id|i,d<iφ(d))=i=1nij=2ns(nj)
.
直接杜教篩。

#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
傳送門
題意:求 ni=1f(i) ,且 N23N+2=d|Nf(d)
分析:最基礎的杜教篩,和上面兩題同樣,拿來構造狄利克雷卷積的函數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 最小公倍數之和
傳送門
題意:求 ni=1nj=1lcm(i,j),n<=1e10
分析:ans = ni=1nj=1ij(i,j)=ng=1gngi=1ngj=1ij[(i,j)==1]
f(n)=ni=1nj=1ij[(i,j)==1]
能夠知道

f(n)=1+2i=2nj=1iij[(i,j)==1]=i=1ni2φ(i)

那麼
f(n)=i=1ni2(id|i,d<iφ(d))=i=1i3i=1nd|i,d<iφ(d)
=i=1i3d=1nφ(d)j=2nd(jd)2=i=1i3d=1nφ(d)d2j=2ndj2
=i=1i3j=2nj2f(nj)

化簡到此處,咱們再次回到熟悉的形式,只須要分段枚舉g,而後杜教篩算f函數便可,這裏外面多了一層g的分塊,可是複雜度仍然是 O(n23) 的。

51nod 1238 最大公約數之和
傳送門
題意:求 ni=1nj=1gcd(i,j),n<=1e10
分析:相比上題,本題容易多了。。

ans=i=1nj=1ngcd(i,j)=i=1nj=1nd|i,d|jφ(d)=d=1nφ(d)(nd)2

直接套杜教篩便可。

#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 平均最小公倍數
傳送門
題意:定義 A(n)=ni=1lcm(i,n)n ,給定a、b,求 bi=aA(i)
分析:

A(n)=ni=1lcm(i,n)n=d|ndφ(d)+[d==1]2
。那麼 F(n)=ni=1A(i)=n2+ni=1d|idφ(d)2
咱們首先令 g(n)=2F(n)n=ni=1nij=1jφ(j) ,令 h(n)=ni=1iφ(i)
h(n)的形式與上面的最小公倍數之和一題中求和函數相似,使用相同的辦法就可獲得 h(n)=ni=1i2nj=2jh(nj)
這樣,咱們分塊枚舉g(n),而後杜教篩算h(n)便可。

#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)知足, a<=lcm(x,y)<=b 1<=

相關文章
相關標籤/搜索