zoukankan      html  css  js  c++  java
  • 高中生都能看懂的莫比乌斯反演

    写在前面

    额,标题好像少了一个不字……应该是高中生都不能看懂的莫比乌斯反演

    这是蒟蒻第一次写这么长的博文

    (gyh nb)( ext{OI-Wiki} nb)

    如果觉得写得凑合就点个支持吧(qwq)

    前置知识

    积性函数狄利克雷卷积数论分块(这一篇去找gyh吧我讲也讲不好)(有空慢慢补)

    Mobius函数

    定义

    莫比乌斯函数(mu(n))定义为:

    [mu(n)=left{ egin{aligned} 1, & n=1 \ (-1)^{s}, & n=p_1p_2…p_s(s为n的本质不同的质因子个数)\0,&n有平方因子end{aligned} ight.]

    其中(p_1p_2…,p_s)是不同素数。

    可以看出,当(n)没有平方因子时,(mu(n))非零。

    (mu)也是积性函数。

    性质

    莫比乌斯函数具有如下的性质:

    [sum_{d|n}mu(d)=epsilon(n)=[n=1] ]

    使用狄利克雷卷积来表示,即

    [mu*1=epsilon ]

    证明:

    (n=1)时显然成立。

    (n>1),设(n)(s)个不同的素因子,由于(mu(d) eq0)当且仅当(d)无平方因子,故(d)中每个素因子的指数只能为(0)(1)

    (n)的某个因子(d),有(mu(d)=(-1)^i),则它由(i)个本质不同的质因子构成。因为质因子的总数为(s),所以满足上式的因子数有(C_s^i)个。

    所以我们就可以对于原式,转化为枚举(mu(d))的值,同时运用二项式定理,故有

    [sum_{d|n}mu(d)=sum_{i=0}^{s}C_s^i imes(-1)^i=sum_{i=0}^{s}C_s^i imes(-1)^i imes 1^{s-i}=(1+(-1))^s ]

    该式在(s=0)(n=1)时为1,否则为(0)

    求莫比乌斯函数

    因为莫比乌斯函数是积性函数,所以可以用线性筛

    int n, cnt, p[A], mu[A];
    bool vis[A];
    
    void getmu() {
        mu[1] = 1;
        for (int i = 2; i <= n; i++) {
    	if (!vis[i]) mu[i] = -1, p[++cnt] = i;
    	for (int j = 1; j <= cnt && i * p[j] <= n; j++) {
    	    vis[i * p[j]] = 1;
    	    if (i % p[j] == 0) { mu[i * p[j]] = 0; break; }
    	    mu[i * p[j]] -= mu[i];
    	}
        }
    }
    

    莫比乌斯反演公式

    (f(n))(g(n))为两个数论函数。

    如果有

    [f(n)=sumlimits_{d|n}g(d) ]

    则有

    [g(n)=sumlimits_{d|n}mu(d)f(frac{n}{d}) ]

    证明

    • 法一:对原式做数论变换

      1. (sumlimits_{d|n}g(d))替换(f(n)),即

        [sumlimits_{d|n}mu(d)f(frac{n}{d})=sumlimits_{d|n}mu(d)sumlimits_{k|frac nd}g(k) ]

      2. 变换求和顺序得

        [sumlimits_{k|n}g(k)sumlimits_{d|frac n k}mu(frac nk) ]

      3. 因为(sumlimits_{d|n}mu(d)=[n=1]),所以只有在(frac{n}{k}=1)(n=k)时才会成立,所以上式等价于

        [sumlimits_{d|n}[n=k]g(k)=g(n) ]

      得证

    • 法二:利用卷积

      原问题为:已知(f=g*1),证明(g=f*mu)

      转化:(f*mu=g*1*mu=g*varepsilon=g)(1*mu=varepsilon)

      再次得证= =

    小性质

    ([gcd(i,j)=1]Leftrightarrowsumlimits_{d|gcd(i,j)}mu(d))

    证明

    • 法一:

      (n=gcd(i,j)),那么等式右边(=sumlimits_{d|n}mu(d)=[n=1]=[gcd(i,j)=1]=)等式左边

    • 法二:

      利用单位函数暴力拆开:([gcd(i,j)=1]=varepsilon(gcd(i,j))=sumlimits_{d|gcd(i,j)}mu(d))

    做题思路&&应用

    利用狄利克雷卷积可以解决一系列求和问题。常见做法是使用一个狄利克雷卷积替换求和式中的一部分,然后调换求和顺序,最终降低时间复杂度。

    经常利用的卷积有(mu*1=epsilon)( ext{Id}=varphi*1)

    还是以题为主吧,但是做的题也会单独写题解,毕竟要多水几篇博客的嘛/huaji

    洛谷 P2522 [HAOI2011]Problem b

    题目链接

    我的题解

    (n)组询问,每次给出(a,b,c,d,k),求(sumlimits_{x=a}^{b}sumlimits_{y=c}^{d}[gcd(x,y)=k])

    (f(n,m)=sumlimits_{i=1}^{n}sumlimits_{j= 1}^{m}[gcd(i,j)=k])

    那么根据容斥原理,题目中的式子就转化成了(f(b,d)-f(b, c - 1) - f(a - 1,d) + f(a - 1, c - 1))

    所以我们接下来的问题就转化为了如何求(f)的值

    现在来化简(f)的值

    1. 容易得出原式等价于$$sumlimits_{i = 1}^{lfloorfrac{n}{k} floor}sumlimits_{j = 1}^{lfloorfrac{m}{k} floor}[gcd(i,j) = 1]$$

    2. 因为(epsilon(n) =sumlimits_{d|n}mu(d)=[n=1]),由此可将原式化为

      [sumlimits_{i=1}^{lfloorfrac{n}{k} floor}sumlimits_{j=1}^{lfloorfrac{m}{k} floor}sumlimits_{d|gcd(i,j)}mu(d) ]

    3. 改变枚举对象并改变枚举顺序,先枚举(d),得

      [sumlimits_{d=1}^{min(n,m)}mu(d)sumlimits_{i=1}^{lfloorfrac{n}{k} floor}[d|i]sumlimits_{j=1}^{lfloorfrac{m}{k} floor}[d|j] ]

      也就是说当(d|i)(d|j)时,(d|gcd(i,j))

    4. 易得(1sim lfloorfrac{n}{k} floor)中一共有(lfloorfrac{n}{dk} floor)(d)的倍数,同理(1sim lfloorfrac{m}{k} floor)中一共有(lfloorfrac{m}{dk} floor)(d)的倍数,于是原式化为$$sumlimits_{d=1}^{min(n,m)}mu(d)lfloorfrac{n}{dk} floorlfloorfrac{m}{dk} floor$$

    此时已经可以(O(n))求解,但是过不了,因为有很多值相同的区间,所以可以用数论分块来做

    先预处理(mu),再用数论分块,复杂度(O(n+Tsqrt n))

    我的代码每次得分玄学,看评测机心情,建议自己写

    /*
    Author:loceaner
    */
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int A = 1e6 + 11;
    const int B = 1e6 + 11;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
    	char c = getchar(); int x = 0, f = 1;
    	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    	return x * f;
    }
    
    int n, a, b, c, d, k, cnt, p[A], mu[A], sum[A];
    bool vis[A];
    
    void getmu() {
    	int MAX = 50010;
    	mu[1] = 1;
    	for (int i = 2; i <= MAX; i++) {
    		if (!vis[i]) mu[i] = -1, p[++cnt] = i;
    		for (int j = 1; j <= cnt && i * p[j] <= MAX; j++) {
    			vis[i * p[j]] = true;
    			if (i % p[j] == 0) break;
    			mu[i * p[j]] -= mu[i];
    		}
    	}
    	for (int i = 1; i <= MAX; i++) sum[i] = sum[i - 1] + mu[i];
    }
    
    int work(int x, int y) {
    	int ans = 0ll;
    	int max = min(x, y);
    	for (int l = 1, r; l <= max; l = r + 1) {
    		r = min(x / (x / l), y / (y / l));
    		ans += (1ll * x / (l * k)) * (1ll * y / (l * k)) * 1ll * (sum[r] - sum[l - 1]); 
    	}
    	return ans;
    }
    
    void solve() {
    	a = read(), b = read(), c = read(), d = read(), k = read();
    	cout << work(b, d) - work(a - 1, d) - work(b, c - 1) + work(a - 1, c - 1) << '
    ';
    }
    
    signed main() {
    	getmu();
    	int T = read();
    	while (T--) solve();
    	return 0;
    }
    

    洛谷 P3455 [POI2007]ZAP-Queries

    题目链接

    我的题解

    (T)组询问,每次询问求

    [sumlimits_{x=1}^{a}sumlimits_{y=1}^{b}[gcd(x,y)=d] ]

    因为我不喜欢用(x、y、a、b、d),所以一一对应换成(i、j、n、m、k)

    直接淦式子

    [egin{align*}&sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}[gcd(i,j)=k]\=& sumlimits_{i=1}^{lfloor frac nk floor}sumlimits_{j=1}^{lfloorfrac mk floor}[gcd(i,j)=1]\=&sumlimits_{i=1}^{lfloor frac nk floor}sumlimits_{j=1}^{lfloorfrac mk floor}sumlimits_{d|gcd(i,j)}mu(d)\=&sumlimits_{i=1}^{lfloor frac nk floor}sumlimits_{j=1}^{lfloorfrac mk floor}sumlimits_{d|i}sum _{d|j}mu(d)\=&sumlimits_{d=1}^{min(lfloorfrac nk floor,lfloorfrac mk floor)}mu(d)sumlimits_{i=1}^{lfloor frac nk floor}sumlimits_{j=1}^{lfloorfrac mk floor}sumlimits_{d|i}sumlimits_{d|j}1\=&sumlimits_{d=1}^{min(lfloorfrac nk floor,lfloorfrac mk floor)}mu(d)sumlimits_{i=1}^{lfloor frac nk floor}sumlimits_{d|i}1sumlimits_{j=1}^{lfloorfrac mk floor}sumlimits_{d|j}1\=&sumlimits_{d=1}^{min(lfloorfrac nk floor,lfloorfrac mk floor)}mu(d)lfloorfrac{lfloor frac nk floor}d floorlfloorlfloorfrac{frac mk floor}d floor\=&sumlimits_{d=1}^{min(lfloorfrac nk floor,lfloorfrac mk floor)}mu(d)lfloorfrac n{kd} floorlfloorfrac m{kd} floorend{align*} ]

    现在就可以每次询问(O(n))做这道题了

    但是跑不过啊,不过显然可以数论分块,所以我们就可以(O(sqrt n))回答每次询问了

    /*
    Author:loceaner
    */
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int A = 5e5 + 11;
    const int B = 1e6 + 11;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
    	char c = getchar(); int x = 0, f = 1;
    	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    	return x * f;
    }
    
    int n, m, k, mu[A], p[A], sum[A], cnt;
    bool vis[A];
    
    void getmu(int n) {
    	mu[1] = 1;
    	for (int i = 2; i <= n; i++) {
    		if (!vis[i]) p[++cnt] = i, mu[i] = -1;
    		for (int j = 1; j <= cnt && i * p[j] <= n; j++) {
    			vis[i * p[j]] = 1;
    			if (i % p[j] == 0) break;
    			mu[i * p[j]] -= mu[i];
    		}
    	}
    	for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i];
    }
    
    int solve(int n, int m, int k) {
    	int ans = 0, maxn = min(n, m);
    	for (int l = 1, r; l <= maxn; l = r + 1) {
    		r = min(n / (n / l), m / (m / l));
    		ans += (sum[r] - sum[l - 1]) * (n / (k * l)) * (m / (k * l));
    	}
    	return ans;
    }
    
    int main() {
    	getmu(50000);
    	int T = read();
    	while (T--) {
    		n = read(), m = read(), k = read();
    		cout << solve(n, m, k) << '
    ';
    	}
    	return 0;
    }
    

    洛谷 P1829 [国家集训队]Crash的数字表格 / JZPTAB

    题目链接

    我的题解

    [sumlimits_{i=1}^{n}sumlimits_{j=1}^{m} ext{lcm}(i,j)(mod 20101009) ]

    容易想到原式等价于

    [sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}frac{i* j}{gcd(i,j)} ]

    枚举(i,j)的最大公约数(d),显然(gcd(frac id,frac jd)=1),即(frac id)(frac jd)互质

    [sumlimits_{i=1}^{n}sumlimits_{j=1}^msumlimits_{d|i,d|j,gcd(frac id,frac jd)=1}frac{i*j}d ]

    变换求和顺序

    [sumlimits_{d=1}^{n}dsumlimits_{i=1}^{lfloorfrac{n}{d} floor}sumlimits_{j=1}^{lfloorfrac{m}{d} floor}[gcd(i,j)=1]i*j ]

    (sum(n,m)=sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}[gcd(i,j)=1]i*j)

    对其进行化简,用(varepsilon(gcd(i,j)))替换([gcd(i,j)=1])

    [sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}sumlimits_{d|gcd(i,j)}mu(d)*i*j ]

    转化为首先枚举约数

    [sumlimits_{d=1}^{min(n,m)}sumlimits_{d|i}^{n}sumlimits_{d|j}^{m}mu(d)*i*j ]

    (i=i'*d,j=j'*d),则可以进一步转化

    [sumlimits_{d=1}^{min(n,m)}mu(d)*d^2*sumlimits_{i=1}^{lfloorfrac{n}{d} floor}sumlimits_{j=1}^{lfloorfrac{m}{d} floor}i*j ]

    前半段可以处理前缀和,后半段可以(O(1))求,设

    [Q(n,m)=sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}i*j=frac{n*(n+1)}{2}*frac{m*(m+1)}{2} ]

    显然可以(O(1))求解

    到现在

    [sum(n,m)=sumlimits_{d=1}^{min(n,m)}mu(d)*d^2*Q(lfloorfrac nd floor,lfloorfrac md floor) ]

    可以用数论分块求解

    回带到原式中

    [sumlimits_{d=1}^{min(n, m)}d*sum(lfloorfrac nd floor,lfloorfrac md floor) ]

    又可以数论分块求解了

    然后就做完啦

    /*
    Author:loceaner
    */
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define int long long
    using namespace std;
    
    const int A = 1e7 + 11;
    const int B = 1e6 + 11;
    const int mod = 20101009;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
        char c = getchar(); int x = 0, f = 1;
        for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
        for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
        return x * f;
    }
    
    bool vis[A];
    int n, m, mu[A], p[B], sum[A], cnt;
    
    void getmu() {
        mu[1] = 1;
        int k = min(n, m);
        for (int i = 2; i <= k; i++) {
            if (!vis[i]) p[++cnt] = i, mu[i] = -1;
            for (int j = 1; j <= cnt && i * p[j] <= k; ++j) {
                vis[i * p[j]] = 1;
                if (i % p[j] == 0) break;
                mu[i * p[j]] = -mu[i];
            }
        }
        for (int i = 1; i <= k; i++) sum[i] = (sum[i - 1] + i * i % mod * mu[i]) % mod;
    }
    
    int Sum(int x, int y) { 
    	return (x * (x + 1) / 2 % mod) * (y * (y + 1) / 2 % mod) % mod; 
    }
    
    int solve2(int x, int y) {
        int res = 0;
        for (int i = 1, j; i <= min(x, y); i = j + 1) {
            j = min(x / (x / i), y / (y / i));
            res = (res + 1LL * (sum[j] - sum[i - 1] + mod) * Sum(x / i, y / i) % mod) % mod;
        }
        return res;
    }
    
    int solve(int x, int y) {
        int res = 0;
        for (int i = 1, j; i <= min(x, y); i = j + 1) {
            j = min(x / (x / i), y / (y / i));
            res = (res + 1LL * (j - i + 1) * (i + j) / 2 % mod * solve2(x / i, y / i) % mod) % mod;
        }
        return res;
    }
    
    signed main() {
        n = read(), m = read();
        getmu();
        cout << solve(n, m) << '
    ';
    }
    

    洛谷 P2257 YY的GCD

    题目链接

    给定(n,m),求二元组((x,y))的个数,满足(1leq xleq n,1leq yleq m),且(gcd(x,y))是素数。

    (n,mleq 10^7),自带多组数据,至多(10^{4})组数据。

    思路与第一题Problem B类似,在这里不再赘述,只给出代码= =

    #include <cmath> 
    #include <cstdio>
    #include <cstring> 
    #include <iostream>
    using namespace std;
    
    const int A = 1e7 + 11; 
    
    inline int read() {
    	char c = getchar(); int x = 0, f = 1;
    	for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; 
    	for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    	return x * f;
    }
    
    bool vis[A];
    long long sum[A];
    int prim[A], mu[A], g[A], cnt, n, m;
    
    void get_mu(int n) {
        mu[1] = 1;
        for (int i = 2; i <= n; i++) {
            if (!vis[i]) {
                mu[i] = -1;
                prim[++cnt] = i;
            }
            for (int j = 1; j <= cnt && prim[j] * i <= n; j++) {
                vis[i * prim[j]] = 1;
                if (i % prim[j] == 0) break;
                else mu[prim[j] * i] = - mu[i];
            }
        }
        for (int j = 1; j <= cnt; j++)
            for (int i = 1; i * prim[j] <= n; i++) g[i * prim[j]] += mu[i];
        for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + (long long)g[i];
    }
    
    signed main() {
        int t = read();
        get_mu(10000000);
        while (t--) {
            n = read(), m = read();
            if (n > m) swap(n, m);
            long long ans = 0;
            for (int l = 1, r; l <= n; l = r + 1) {
                r = min(n / (n / l), m / (m / l));
                ans += 1ll * (n / l) * (m / l) * (sum[r] - sum[l - 1]);
            }
            cout << ans << '
    ';
        }
        return 0;
    }
    

    洛谷 P3327 [SDOI2015]约数个数和

    题目链接

    [sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}d(ij) ]

    (d(x))(x)的约数个数和

    需要用到

    [d(ij)=sumlimits_{x|i}sumlimits_{y|j}[gcd(x,y)=1] ]

    证明我也不会

    然后自己推导吧,在此不再赘述

    /*
    Author:loceaner
    */
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int A = 5e5 + 11;
    const int B = 1e6 + 11;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
    	char c = getchar(); int x = 0, f = 1;
    	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    	return x * f;
    }
    
    bool vis[A];
    int n, m, p[A], mu[A], cnt, sum[A];
    long long g[A], ans;
    
    void getmu(int n) {
    	mu[1] = 1;
    	for (int i = 2; i <= n; i++) {
    		if (!vis[i]) mu[i] = -1, p[++cnt] = i;
    		for (int j = 1; j <= cnt && i * p[j] <= n; j++) {
    			vis[i * p[j]] = 1;
    			if (i % p[j] == 0) break;
    			mu[i * p[j]] -= mu[i];
    		}
    	}
    	for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i];
    	for (int i = 1; i <= n; i++) {
    		int ans = 0;
    		for (int l = 1, r; l <= i; l = r + 1) {
    			r = (i / (i / l));
    			ans += 1ll * (r - l + 1) * (i / l);
    		}
    		g[i] = ans;
    	}
    }
    
    signed main() {
    	int T = read();
    	getmu(50000);
    	while (T--) {
    		n = read(), m = read();
    		int maxn = min(n, m);
    		ans = 0;
    		for (int l = 1, r; l <= maxn; l = r + 1) {
    			r = min(n / (n / l), m / (m / l));
    			ans += 1ll * (sum[r] - sum[l - 1]) * 1ll * g[n / l] * 1ll * g[m / l];
    		}
    		cout << ans << '
    ';
    	}
    	return 0;
    }
    

    洛谷 P4449 于神之怒加强版

    [sum_{i=1}^{n}sum_{j=1}^{m}gcd(i,j)^k(mod 1e9+7) ]

    还是直接淦式子

    [egin{align*}&sum_{i=1}^{n}sum_{j=1}^{m}gcd(i,j)^k\=&sum_{i=1}^{n}sum_{j=1}^{m}sum_{d=1}^{min(n,m)}d^k[gcd(i,j)=d]\=&sum_{d=1}^{min(n,m)}d^ksum_{i=1}^{lfloorfrac nd floor}sum_{j=1}^{lfloorfrac md floor}[gcd(i,j)=1]\=&sum_{d=1}^{min(n,m)}d^ksum_{i=1}^{lfloorfrac nd floor}sum_{j=1}^{lfloorfrac md floor}sum_{x|gcd(i,j)}mu(x)\=&sum_{d=1}^{min(n,m)}d^ksum_{i=1}^{lfloorfrac nd floor}sum_{j=1}^{lfloorfrac md floor}sum_{x|i,x|j}mu(x)\=&sum_{d=1}^{min(n,m)}d^ksum_{x=1}^{min(lfloorfrac nd floor,lfloorfrac md floor)}mu(x)sum_{i=1}^{lfloorfrac nd floor}[x|i]sum_{j=1}^{lfloorfrac md floor}[x|j]\=&sum_{d=1}^{min(n,m)}d^ksum_{x=1}^{min(lfloorfrac nd floor,lfloorfrac md floor)}mu(x)lfloorfrac n{dx} floorlfloorfrac m{dx} floorend{align*} ]

    (P=dx),则原式等于

    [sum_{P=1}^{min(n,m)}lfloorfrac n{P} floorlfloorfrac m{P} floorsum_{d|P}d^kmu(frac Pd) ]

    显然前面的(lfloorfrac n{P} floorlfloorfrac m{P} floor)部分可以分块求解。

    现在考虑后面的一部分,令

    [g(n)=sum_{d|n}d^kmu(frac nd) ]

    容易得出这个函数是积性函数,所以我们就可以线性筛然后求出其前缀和

    然后就做完了

    /*
    Author:loceaner
    莫比乌斯反演
    */
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define int long long
    using namespace std;
    
    const int A = 5e6 + 11;
    const int B = 1e6 + 11;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
    	char c = getchar();
    	int x = 0, f = 1;
    	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    	return x * f;
    }
    
    bool vis[A];
    int T, n, m, k, f[A], g[A], p[A], cnt, sum[A];
    
    inline int power(int a, int b) {
    	int res = 1;
    	while (b) {
    		if (b & 1) res = res * a % mod;
    		a = a * a % mod, b >>= 1;
    	}
    	return res;
    }
    
    inline int mo(int x) {
    	if(x > mod) x -= mod;
    	return x;
    }
    
    inline void work() {
    	g[1] = 1;
    	int maxn = 5e6 + 1;
    	for (int i = 2; i <= maxn; i++) {
    		if (!vis[i]) { p[++cnt] = i, f[cnt] = power(i, k), g[i] = mo(f[cnt] - 1 + mod); }
    		for (int j = 1; j <= cnt && i * p[j] <= maxn; j++) {
    			vis[i * p[j]] = 1;
    			if (i % p[j] == 0) { g[i * p[j]] = g[i] * 1ll * f[j] % mod; break; }
    			g[i * p[j]] = g[i] * 1ll * g[p[j]] % mod;
    		}
    	}
    	for (int i = 2; i <= maxn; i++) g[i] = (g[i - 1] + g[i]) % mod;
    }
    
    inline int abss(int x) {
    	while (x < 0) x += mod;
    	return x;
    }
    
    signed main() {
    	T = read(), k = read();
    	work();
    	while (T--) {
    		n = read(), m = read();
    		int maxn = min(n, m), ans = 0;
    		for (int l = 1, r; l <= maxn; l = r + 1) {
    			r = min(n / (n / l), m / (m / l));
    			(ans += abss(g[r] - g[l - 1]) * 1ll * (n / l) % mod * (m / l) % mod) %= mod;
    		}
    		ans = (ans % mod + mod) % mod;
    		cout << ans << '
    ';
    	}
    	return 0;
    }
    
  • 相关阅读:
    windows ntstatus.h 头文件
    Android Q 后台启动 Activity
    windows 删除文件夹所有文件夹及文件代码
    Android 加壳App Demo
    Android App 签名保护demo
    RXAndroidBle 记录网址
    c++ windows 获取系统时间
    js 代码保存
    day33 ansible
    day31 综合实时同步服务
  • 原文地址:https://www.cnblogs.com/loceaner/p/12785174.html
Copyright © 2011-2022 走看看