数学专题训练3 数论1

1. Problem - 27E - Codeforces

给定 n ( 1 ≤ n ≤ 1000 ) n(1 \le n \le 1000) n(1n1000)​​​,找到因子个数恰好为 n n n​​​ 个的最小正整数. 保证答案不大于 1 e 18 1e18 1e18.

反素数 的思路是一样的,深搜

这个是枚举当前数字可以填的最高幂指数,后面的数的最高幂指数都不会比当前高。因为如果比之前的质数幂指数高,交换二者的次数,乘积会变小,因子个数却不变. 用上这个性质,速度可以提升非常非常多!

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;

ull prime[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};

int n;
//一定要开大点,不然会错.
ull prod = ~0ull;

void dfs(int id, int up, ull res, int cnt)
{
    if(cnt > n || id >= 16) return;  //剪枝
    if(cnt == n && res < prod)
    {
        prod = res;
        return;  //剪枝
    }
    for(int i = 1; i <= up; i++)
    {
        //保证幂次成不升序列,剪枝
        if(res * prime[id] > prod) break;
        dfs(id + 1, i, res = res * prime[id], cnt * (i + 1));
    }
}
int main()
{
    scanf("%d", &n);
    dfs(0, 64, 1, 1);
    printf("%llu\n", prod);
    return 0;
}

2. Problem b

求值(多组数据)
∑ i = x n ∑ j = y m [ gcd ⁡ ( i , j ) = k ] ( 1 ⩽ T , x , y , n , m , k ⩽ 5 × 1 0 4 ) \sum_{i=x}^{n}\sum_{j=y}^{m}[\gcd(i,j)=k]\qquad (1\leqslant T,x,y,n,m,k\leqslant 5\times 10^4) i=xnj=ym[gcd(i,j)=k](1T,x,y,n,m,k5×104)

根据容斥原理,原式可以分成 4 4 4 块来处理,每一块的式子都为

∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) = k ] \sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=k] i=1nj=1m[gcd(i,j)=k]

考虑化简该式子

∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ [ gcd ⁡ ( i , j ) = 1 ] \sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}[\gcd(i,j)=1] i=1knj=1km[gcd(i,j)=1]

因为 gcd ⁡ ( i , j ) = 1 \gcd(i,j)=1 gcd(i,j)=1 时对答案才用贡献,于是我们可以将其替换为 ε ( gcd ⁡ ( i , j ) ) \varepsilon(\gcd(i,j)) ε(gcd(i,j)) ε ( n ) \varepsilon(n) ε(n) 当且仅当 n = 1 n=1 n=1 时值为 1 1 1 否则为 0 0 0),故原式化为

∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ ε ( gcd ⁡ ( i , j ) ) \sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}\varepsilon(\gcd(i,j)) i=1knj=1kmε(gcd(i,j))

ε \varepsilon ε 函数展开得到

∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ ∑ d ∣ gcd ⁡ ( i , j ) μ ( d ) \displaystyle\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}\sum_{d\mid \gcd(i,j)}\mu(d) i=1knj=1kmdgcd(i,j)μ(d)

变换求和顺序,先枚举 d ∣ gcd ⁡ ( i , j ) d\mid \gcd(i,j) dgcd(i,j) 可得

∑ d = 1 μ ( d ) ∑ i = 1 ⌊ n k ⌋ [ d ∣ i ] ∑ j = 1 ⌊ m k ⌋ [ d ∣ j ] \displaystyle\sum_{d=1}\mu(d)\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}[d\mid i]\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}[d\mid j] d=1μ(d)i=1kn[di]j=1km[dj]

易知 1 ∼ ⌊ n k ⌋ 1\sim\lfloor\dfrac{n}{k}\rfloor 1kn d d d 的倍数有 ⌊ n k d ⌋ \lfloor\dfrac{n}{kd}\rfloor kdn 个,故原式化为

∑ d = 1 min ⁡ ( ⌊ n k ⌋ , ⌊ m k ⌋ ) μ ( d ) ⌊ n k d ⌋ ⌊ m k d ⌋ \displaystyle\sum_{d=1}^{\min(\lfloor \frac{n}{k}\rfloor,\lfloor \frac{m}{k}\rfloor)}\mu(d)\lfloor\frac{n}{kd}\rfloor\lfloor\frac{m}{kd}\rfloor d=1min(⌊kn,km⌋)μ(d)kdnkdm

很显然,式子可以数论分块求解。

#include<bits/stdc++.h>
using namespace std;
const int N = 50010;
int prime[N], mu[N], cnt, sum[N];
bool st[N];
void sieve(int n)
{
    mu[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        if(!st[i]) prime[cnt++] = i, mu[i] = -1;
        for(int j = 0; prime[j] <= n / i; j++)
        {
            st[i * prime[j]] = true;
            mu[i * prime[j]] = -mu[i];
            if(i % prime[j] == 0)
            {
                mu[i * prime[j]] = 0;
                break;
            }
        }
    }
    for(int i = 1; i <= n; i++)
    {
        sum[i] = sum[i - 1] + mu[i];
    }
}

int get(int n, int i)
{
    return n / (n / i);
}

int solve(int n, int m)
{
    int res = 0;
    for(int l = 1, r; l <= min(n, m); l = r + 1)
    {
        r = min(get(n, l), get(m, l));
        res += (sum[r] - sum[l - 1]) * (n / l) * (m / l);
    }
    return res;
}
int main()
{
    sieve(N - 1);
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int a, b, c, d, k;
        scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
        printf("%d\n", solve(b / k, d / k) - solve((a - 1) / k, d / k)
               - solve(b / k, (c - 1) / k) + solve((a - 1) / k, (c - 1) / k));
    }
    return 0;
}

3. LCM Sum

求值(多组数据)

∑ i = 1 n lcm ⁡ ( i , n ) s.t.  1 ⩽ T ⩽ 3 × 1 0 5 , 1 ⩽ n ⩽ 1 0 6 \sum_{i=1}^n \operatorname{lcm}(i,n)\quad \text{s.t.}\ 1\leqslant T\leqslant 3\times 10^5,1\leqslant n\leqslant 10^6 i=1nlcm(i,n)s.t. 1T3×105,1n106

易得原式即

∑ i = 1 n i ⋅ n gcd ⁡ ( i , n ) \sum_{i=1}^n \frac{i\cdot n}{\gcd(i,n)} i=1ngcd(i,n)in

将原式复制一份并且颠倒顺序,然后将 n 一项单独提出,可得

1 2 ⋅ ( ∑ i = 1 n − 1 i ⋅ n gcd ⁡ ( i , n ) + ∑ i = n − 1 1 i ⋅ n gcd ⁡ ( i , n ) ) + n \frac{1}{2}\cdot \left(\sum_{i=1}^{n-1}\frac{i\cdot n}{\gcd(i,n)}+\sum_{i=n-1}^{1}\frac{i\cdot n}{\gcd(i,n)}\right)+n 21(i=1n1gcd(i,n)in+i=n11gcd(i,n)in)+n

根据 gcd ⁡ ( i , n ) = gcd ⁡ ( n − i , n ) \gcd(i,n)=\gcd(n-i,n) gcd(i,n)=gcd(ni,n),可将原式化为

1 2 ⋅ ( ∑ i = 1 n − 1 i ⋅ n gcd ⁡ ( i , n ) + ∑ i = n − 1 1 i ⋅ n gcd ⁡ ( n − i , n ) ) + n \frac{1}{2}\cdot \left(\sum_{i=1}^{n-1}\frac{i\cdot n}{\gcd(i,n)}+\sum_{i=n-1}^{1}\frac{i\cdot n}{\gcd(n-i,n)}\right)+n 21(i=1n1gcd(i,n)in+i=n11gcd(ni,n)in)+n

两个求和式中分母相同的项可以合并。

1 2 ⋅ ∑ i = 1 n − 1 n 2 gcd ⁡ ( i , n ) + n \frac{1}{2}\cdot \sum_{i=1}^{n-1}\frac{n^2}{\gcd(i,n)}+n 21i=1n1gcd(i,n)n2+n

1 2 ⋅ ∑ i = 1 n n 2 gcd ⁡ ( i , n ) + n 2 \frac{1}{2}\cdot \sum_{i=1}^{n}\frac{n^2}{\gcd(i,n)}+\frac{n}{2} 21i=1ngcd(i,n)n2+2n

可以将相同的 gcd ⁡ ( i , n ) \gcd(i,n) gcd(i,n) 合并在一起计算,故只需要统计 gcd ⁡ ( i , n ) = d \gcd(i,n)=d gcd(i,n)=d 的个数。当 gcd ⁡ ( i , n ) = d \gcd(i,n)=d gcd(i,n)=d 时, gcd ⁡ ( i d , n d ) = 1 \displaystyle\gcd(\frac{i}{d},\frac{n}{d})=1 gcd(di,dn)=1,所以 gcd ⁡ ( i , n ) = d \gcd(i,n)=d gcd(i,n)=d 的个数有 φ ( n d ) \displaystyle\varphi(\frac{n}{d}) φ(dn) 个。

故答案为

1 2 ⋅ ∑ d ∣ n n 2 ⋅ φ ( n d ) d + n 2 \frac{1}{2}\cdot\sum_{d\mid n}\frac{n^2\cdot\varphi(\frac{n}{d})}{d}+\frac{n}{2} 21dndn2φ(dn)+2n

变换求和顺序,设 d ′ = n d \displaystyle d'=\frac{n}{d} d=dn,合并公因式,式子化为

1 2 n ⋅ ( ∑ d ′ ∣ n d ′ ⋅ φ ( d ′ ) + 1 ) \frac{1}{2}n\cdot\left(\sum_{d'\mid n}d'\cdot\varphi(d')+1\right) 21n dndφ(d)+1

#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
typedef long long ll;

int prime[N], phi[N], cnt;
ll g[N];
bool st[N];

void sieve(int n)
{
    phi[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        if(!st[i])
        {
            prime[cnt++] = i;
            phi[i] = i - 1;
        }
        for(int j = 0; prime[j] <= n / i; j++)
        {
            st[i * prime[j]] = true;
            if(i % prime[j] == 0)
            {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = i; j <= n; j += i)
        {
            g[j] += i * phi[i];
        }
    }
    for(int i = 1; i <= n; i++)
    {
        g[i] = (g[i] + 1) * i / 2;
    }
}
int main()
{
    sieve(N - 1);
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n;
        scanf("%d", &n);
        printf("%lld\n", g[n]);
    }
    return 0;
}

实际上可以通过巧妙的筛法线性筛出

g ⁡ ( n ) = ∑ d ∣ n d ⋅ φ ( d ) \displaystyle \operatorname{g}(n)=\sum_{d\mid n} d\cdot\varphi(d) g(n)=dndφ(d),已知 g ⁡ \operatorname{g} g 为积性函数,于是可以 Θ ( n ) \Theta(n) Θ(n) 筛出。每次询问 Θ ( 1 ) \Theta(1) Θ(1) 计算即可。

下面给出这个函数筛法的推导过程:

首先考虑 g ⁡ ( p j k ) \operatorname g(p_j^k) g(pjk) 的值,显然它的约数只有 p j 0 , p j 1 , ⋯   , p j k p_j^0,p_j^1,\cdots,p_j^k pj0,pj1,,pjk,因此

g ⁡ ( p j k ) = ∑ w = 0 k p j w ⋅ φ ( p j w ) \operatorname g(p_j^k)=\sum_{w=0}^{k}p_j^w\cdot\varphi(p_j^w) g(pjk)=w=0kpjwφ(pjw)

又有 φ ( p j w ) = p j w − 1 ⋅ ( p j − 1 ) \varphi(p_j^w)=p_j^{w-1}\cdot(p_j-1) φ(pjw)=pjw1(pj1),则原式可化为

∑ w = 0 k p j 2 w − 1 ⋅ ( p j − 1 ) \sum_{w=0}^{k}p_j^{2w-1}\cdot(p_j-1) w=0kpj2w1(pj1)

于是有

g ⁡ ( p j k + 1 ) = g ⁡ ( p j k ) + p j 2 k + 1 ⋅ ( p j − 1 ) \operatorname g(p_j^{k+1})=\operatorname g(p_j^k)+p_j^{2k+1}\cdot(p_j-1) g(pjk+1)=g(pjk)+pj2k+1(pj1)

那么,对于线性筛中的 g ⁡ ( i ⋅ p j ) ( p j ∣ i ) \operatorname g(i\cdot p_j)(p_j|i) g(ipj)(pji),令 i = a ⋅ p j w ( gcd ⁡ ( a , p j ) = 1 ) i=a\cdot p_j^w(\operatorname{gcd}(a,p_j)=1) i=apjw(gcd(a,pj)=1),可得

g ⁡ ( i ⋅ p j ) = g ⁡ ( a ) ⋅ g ⁡ ( p j w + 1 ) \operatorname g(i\cdot p_j)=\operatorname g(a)\cdot\operatorname g(p_j^{w+1}) g(ipj)=g(a)g(pjw+1)

g ⁡ ( i ) = g ⁡ ( a ) ⋅ g ⁡ ( p j w ) \operatorname g(i)=\operatorname g(a)\cdot\operatorname g(p_j^w) g(i)=g(a)g(pjw)

g ⁡ ( i ⋅ p j ) − g ⁡ ( i ) = g ⁡ ( a ) ⋅ p j 2 w + 1 ⋅ ( p j − 1 ) \operatorname g(i\cdot p_j)-\operatorname g(i)=\operatorname g(a)\cdot p_j^{2w+1}\cdot(p_j-1) g(ipj)g(i)=g(a)pj2w+1(pj1)

同理有

g ⁡ ( i ) − g ⁡ ( i p j ) = g ⁡ ( a ) ⋅ p j 2 w − 1 ⋅ ( p j − 1 ) \operatorname g(i)-\operatorname g(\frac{i}{p_j})=\operatorname g(a)\cdot p_j^{2w-1}\cdot(p_j-1) g(i)g(pji)=g(a)pj2w1(pj1)

因此

g ⁡ ( i ⋅ p j ) = g ⁡ ( i ) + ( g ⁡ ( i ) − g ⁡ ( i p j ) ) ⋅ p j 2 \operatorname g(i\cdot p_j)=\operatorname g(i)+\left (\operatorname g(i)-\operatorname g(\frac{i}{p_j})\right )\cdot p_j^2 g(ipj)=g(i)+(g(i)g(pji))pj2

4. Crash的数字表格

求值(对 20101009 20101009 20101009 取模)

∑ i = 1 n ∑ j = 1 m lcm ⁡ ( i , j ) ( n , m ⩽ 1 0 7 ) \sum_{i=1}^n\sum_{j=1}^m\operatorname{lcm}(i,j)\qquad (n,m\leqslant 10^7) i=1nj=1mlcm(i,j)(n,m107)

易知原式等价于

∑ i = 1 n ∑ j = 1 m i ⋅ j gcd ⁡ ( i , j ) \sum_{i=1}^n\sum_{j=1}^m\frac{i\cdot j}{\gcd(i,j)} i=1nj=1mgcd(i,j)ij

枚举最大公因数 d d d,显然两个数除以 d d d 得到的数互质

∑ i = 1 n ∑ j = 1 m ∑ d ∣ i , d ∣ j , gcd ⁡ ( i d , j d ) = 1 i ⋅ j d \sum_{i=1}^n\sum_{j=1}^m\sum_{d\mid i,d\mid j,\gcd(\frac{i}{d},\frac{j}{d})=1}\frac{i\cdot j}{d} i=1nj=1mdi,dj,gcd(di,dj)=1dij

非常经典的 gcd ⁡ \gcd gcd 式子的化法

∑ d = 1 n d ⋅ ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ gcd ⁡ ( i , j ) = 1 ]   i ⋅ j \sum_{d=1}^n d\cdot\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}[\gcd(i,j)=1]\ i\cdot j d=1ndi=1dnj=1dm[gcd(i,j)=1] ij

后半段式子中,出现了互质数对之积的和,为了让式子更简洁就把它拿出来单独计算。于是我们记

sum ⁡ ( n , m ) = ∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) = 1 ]   i ⋅ j \operatorname{sum}(n,m)=\sum_{i=1}^n\sum_{j=1}^m [\gcd(i,j)=1]\ i\cdot j sum(n,m)=i=1nj=1m[gcd(i,j)=1] ij

接下来对 sum ⁡ ( n , m ) \operatorname{sum}(n,m) sum(n,m) 进行化简。首先枚举约数,并将 [ gcd ⁡ ( i , j ) = 1 ] [\gcd(i,j)=1] [gcd(i,j)=1] 表示为 ε ( gcd ⁡ ( i , j ) ) \varepsilon(\gcd(i,j)) ε(gcd(i,j))

∑ d = 1 n ∑ d ∣ i n ∑ d ∣ j m μ ( d ) ⋅ i ⋅ j \sum_{d=1}^n\sum_{d\mid i}^n\sum_{d\mid j}^m\mu(d)\cdot i\cdot j d=1ndindjmμ(d)ij

i = i ′ ⋅ d i=i'\cdot d i=id j = j ′ ⋅ d j=j'\cdot d j=jd,显然式子可以变为

∑ d = 1 n μ ( d ) ⋅ d 2 ⋅ ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ i ⋅ j \sum_{d=1}^n\mu(d)\cdot d^2\cdot\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}i\cdot j d=1nμ(d)d2i=1dnj=1dmij

观察上式,前半段可以预处理前缀和;后半段又是一个范围内数对之和,记

g ( n , m ) = ∑ i = 1 n ∑ j = 1 m i ⋅ j = n ⋅ ( n + 1 ) 2 × m ⋅ ( m + 1 ) 2 g(n,m)=\sum_{i=1}^n\sum_{j=1}^m i\cdot j=\frac{n\cdot(n+1)}{2}\times\frac{m\cdot(m+1)}{2} g(n,m)=i=1nj=1mij=2n(n+1)×2m(m+1)

可以 Θ ( 1 ) \Theta(1) Θ(1) 求解

至此

sum ⁡ ( n , m ) = ∑ d = 1 n μ ( d ) ⋅ d 2 ⋅ g ( ⌊ n d ⌋ , ⌊ m d ⌋ ) \operatorname{sum}(n,m)=\sum_{d=1}^n\mu(d)\cdot d^2\cdot g(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor) sum(n,m)=d=1nμ(d)d2g(⌊dn,dm⌋)

我们可以 ⌊ n ⌊ n d ⌋ ⌋ \lfloor\frac{n}{\lfloor\frac{n}{d}\rfloor}\rfloor dnn 数论分块求解 sum ⁡ ( n , m ) \operatorname{sum}(n,m) sum(n,m) 函数。

在求出 sum ⁡ ( n , m ) \operatorname{sum}(n,m) sum(n,m) 后,回到定义 sum ⁡ \operatorname{sum} sum 的地方,可得原式为

∑ d = 1 n d ⋅ sum ⁡ ( ⌊ n d ⌋ , ⌊ m d ⌋ ) \sum_{d=1}^n d\cdot\operatorname{sum}(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor) d=1ndsum(⌊dn,dm⌋)

可见这又是一个可以数论分块求解的式子!

#include<bits/stdc++.h>
using namespace std;
const int N = 10000010;
typedef long long ll;
const ll mod = 20101009;
int prime[N], cnt;
ll mu[N];
bool st[N];

void sieve(int n)
{
    mu[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        if(!st[i]) prime[cnt++] = i, mu[i] = -1;
        for(int j = 0; prime[j] <= n / i; j++)
        {
            st[i * prime[j]] = true;
            if(i % prime[j] == 0) break;
            mu[i * prime[j]] = -mu[i];
        }
    }
    for(int i = 1; i <= n; i++) mu[i] = (1ll * i * i % mod * mu[i] % mod + mu[i - 1] + mod) % mod;
}
ll g(ll n, ll m)
{
    return (n * (n + 1) / 2 % mod) * (m * (m + 1) / 2 % mod) % mod;
}
ll get(ll n, ll l)
{
    return n / (n / l);
}
ll sum(ll n, ll m)
{
    ll res = 0;
    for(ll l = 1, r; l <= min(n, m); l = r + 1)
    {
        r = min(min(n, m), min(get(n, l), get(m, l)));
        res += (mu[r] - mu[l - 1] + mod) % mod * g(n / l, m / l) % mod;
        res %= mod;
    }
    return res;
}
ll solve(ll n, ll m)
{
    ll res = 0;
    for(ll l = 1, r; l <= min(n, m); l = r + 1)
    {
        r = min(min(n, m), min(get(n, l), get(m, l)));
        res += (l + r) * (r - l + 1) / 2 % mod * sum(n / l, m / l) % mod;
        res %= mod;
    }
    return res;
}
int main()
{
    sieve(N - 1);
    ll n, m;
    scanf("%lld%lld", &n, &m);
    printf("%lld\n", solve(n, m));
    return 0;
}

5. 约数个数和

多组数据,求

∑ i = 1 n ∑ j = 1 m d ( i ⋅ j ) d ( n ) = ∑ i ∣ n 1 n , m , T ≤ 5 × 1 0 4 \sum_{i=1}^n\sum_{j=1}^md(i\cdot j)\\ d(n)=\sum_{i \mid n}1\\ n,m,T\leq5\times10^4 i=1nj=1md(ij)d(n)=in1n,m,T5×104
其中 d ( n ) d(n) d(n) 表示 n n n 的约数个数

要推这道题首先要了解 d d d 函数的一个特殊性质

d ( i ⋅ j ) = ∑ x ∣ i ∑ y ∣ j [ gcd ⁡ ( x , y ) = 1 ] d(i\cdot j)=\sum_{x \mid i}\sum_{y \mid j}[\gcd(x,y)=1] d(ij)=xiyj[gcd(x,y)=1]

再化一下这个式子

d ( i ⋅ j ) = ∑ x ∣ i ∑ y ∣ j [ gcd ⁡ ( x , y ) = 1 ] = ∑ x ∣ i ∑ y ∣ j ∑ p ∣ gcd ⁡ ( x , y ) μ ( p ) = ∑ p = 1 m i n ( i , j ) ∑ x ∣ i ∑ y ∣ j [ p ∣ gcd ⁡ ( x , y ) ] ⋅ μ ( p ) = ∑ p ∣ i , p ∣ j μ ( p ) ∑ x ∣ i ∑ y ∣ j [ p ∣ gcd ⁡ ( x , y ) ] = ∑ p ∣ i , p ∣ j μ ( p ) ∑ x ∣ i p ∑ y ∣ j p 1 = ∑ p ∣ i , p ∣ j μ ( p ) ∗ ( ∑ x ∣ i p 1 ) ∗ ( ∑ y ∣ j p 1 ) = ∑ p ∣ i , p ∣ j μ ( p ) d ( i p ) d ( j p ) \begin{aligned} d(i\cdot j)=&\sum_{x \mid i}\sum_{y \mid j}[\gcd(x,y)=1]\\ =&\sum_{x \mid i}\sum_{y \mid j}\sum_{p \mid \gcd(x,y)}\mu(p)\\ =&\sum_{p=1}^{min(i,j)}\sum_{x \mid i}\sum_{y \mid j}[p \mid \gcd(x,y)]\cdot\mu(p)\\ =&\sum_{p \mid i,p \mid j}\mu(p)\sum_{x \mid i}\sum_{y \mid j}[p \mid \gcd(x,y)]\\ =&\sum_{p \mid i,p \mid j}\mu(p)\sum_{x \mid \frac{i}{p}}\sum_{y \mid \frac{j}{p}}1\\ =&\sum_{p \mid i,p \mid j}\mu(p)*(\sum_{x \mid \frac{i}{p}}1) * (\sum_{y \mid \frac{j}{p}}1)\\ =&\sum_{p \mid i,p \mid j}\mu(p)d\left(\frac{i}{p}\right)d\left(\frac{j}{p}\right)\\ \end{aligned} d(ij)=======xiyj[gcd(x,y)=1]xiyjpgcd(x,y)μ(p)p=1min(i,j)xiyj[pgcd(x,y)]μ(p)pi,pjμ(p)xiyj[pgcd(x,y)]pi,pjμ(p)xpiypj1pi,pjμ(p)(xpi1)(ypj1)pi,pjμ(p)d(pi)d(pj)

将上述式子代回原式

∑ i = 1 n ∑ j = 1 m d ( i ⋅ j ) = ∑ i = 1 n ∑ j = 1 m ∑ p ∣ i , p ∣ j μ ( p ) d ( i p ) d ( j p ) = ∑ p = 1 m i n ( n , m ) ∑ i = 1 n ∑ j = 1 m [ p ∣ i , p ∣ j ] ⋅ μ ( p ) d ( i p ) d ( j p ) = ∑ p = 1 m i n ( n , m ) ∑ i = 1 ⌊ n p ⌋ ∑ j = 1 ⌊ m p ⌋ μ ( p ) d ( i ) d ( j ) = ∑ p = 1 m i n ( n , m ) μ ( p ) ∑ i = 1 ⌊ n p ⌋ d ( i ) ∑ j = 1 ⌊ m p ⌋ d ( j ) = ∑ p = 1 m i n ( n , m ) μ ( p ) S ( ⌊ n p ⌋ ) S ( ⌊ m p ⌋ ) ( S ( n ) = ∑ i = 1 n d ( i ) ) \begin{aligned} &\sum_{i=1}^n\sum_{j=1}^md(i\cdot j)\\ =&\sum_{i=1}^n\sum_{j=1}^m\sum_{p \mid i,p \mid j}\mu(p)d\left(\frac{i}{p}\right)d\left(\frac{j}{p}\right)\\ =&\sum_{p=1}^{min(n,m)} \sum_{i=1}^n\sum_{j=1}^m [p \mid i,p \mid j]\cdot\mu(p)d\left(\frac{i}{p}\right)d\left(\frac{j}{p}\right)\\ =&\sum_{p=1}^{min(n,m)} \sum_{i=1}^{\left\lfloor\frac{n}{p}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{p}\right\rfloor} \mu(p)d(i)d(j)\\ =&\sum_{p=1}^{min(n,m)}\mu(p) \sum_{i=1}^{\left\lfloor\frac{n}{p}\right\rfloor}d(i) \sum_{j=1}^{\left\lfloor\frac{m}{p}\right\rfloor}d(j)\\ =&\sum_{p=1}^{min(n,m)}\mu(p) S\left(\left\lfloor\frac{n}{p}\right\rfloor\right) S\left(\left\lfloor\frac{m}{p}\right\rfloor\right) \left(S(n)=\sum_{i=1}^{n}d(i)\right)\\ \end{aligned} =====i=1nj=1md(ij)i=1nj=1mpi,pjμ(p)d(pi)d(pj)p=1min(n,m)i=1nj=1m[pi,pj]μ(p)d(pi)d(pj)p=1min(n,m)i=1pnj=1pmμ(p)d(i)d(j)p=1min(n,m)μ(p)i=1pnd(i)j=1pmd(j)p=1min(n,m)μ(p)S(pn)S(pm)(S(n)=i=1nd(i))

那么 O ( n ) O(n) O(n) 预处理 μ , d \mu,d μ,d 的前缀和, O ( n ) O(\sqrt{n}) O(n ) 分块处理询问,总复杂度 O ( n + T n ) O(n+T\sqrt{n}) O(n+Tn ).

6. C. Curious

题意:计算 ∑ i = 1 n ∑ j = 1 n [ g c d ( a i , a j ) = x ] \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(a_i, a_j) = x] i=1nj=1n[gcd(ai,aj)=x]

T T T 组数据,每组数据 k k k 组询问,每次询问问一个 x x x.

1 ≤ T ≤ 10 , 1 ≤ k , n , m ≤ 1 0 5 , 1 ≤ a i ≤ m 1 \le T \le 10, 1 \le k, n, m \le 10^5, 1 \le a_i \le m 1T10,1k,n,m105,1aim

  • 莫比乌斯反演。我们设 f ( x ) = ∑ i = 1 n ∑ j = 1 n [ g c d ( a i , a j ) = x ] f(x) = \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(a_i, a_j) = x] f(x)=i=1nj=1n[gcd(ai,aj)=x],则 F ( x ) = ∑ d ∣ n f ( d ) = ∑ i = 1 n ∑ j = 1 n [ x ∣ g c d ( a i , a j ) ] F(x) = \sum\limits_{d|n}f(d) = \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[x|gcd(a_i, a_j)] F(x)=dnf(d)=i=1nj=1n[xgcd(ai,aj)],设 m = ∑ i = 1 n x ∣ a i m = \sum\limits_{i=1}^{n}x|a_i m=i=1nxai,则 F ( x ) = m 2 F(x) = m^2 F(x)=m2
  • 因此,令 d ′ = d x , m ′ = ∑ i = 1 n d ′ x ∣ a i d' = \frac{d}{x}, m'=\sum\limits_{i=1}^{n}d'x | a_i d=xd,m=i=1ndxai. 则 f ( x ) = ∑ x ∣ d μ ( d x ) m 2 = ∑ d ′ = 1 ∞ μ ( d ′ ) m ′ 2 f(x) = \sum\limits_{x|d}\mu(\frac{d}{x})m^2 = \sum\limits_{d'=1}^{\infty}\mu(d')m'^2 f(x)=xdμ(xd)m2=d=1μ(d)m′2.
  • 有一个细节,就是求 m m m,那个地方有一个小 trick,就是先筛出来 1 ∼ 1 0 5 1 \sim 10^5 1105 范围内的数的约数。这个也很好写。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 100010;
int prime[maxn], mu[maxn], cnt, a[maxn], st[maxn];

vector<int> divisors[maxn];

void sieve(int N) {
	mu[1] = 1;
	for (int i = 2; i <= N; i++) {
		if (!st[i]) st[i] = prime[cnt++] = i, mu[i] = -1;
		for (int j = 0; prime[j] <= N / i; j++) {
			st[i * prime[j]] = prime[j];
			if (i % prime[j] == 0) break;
			mu[i * prime[j]] = -mu[i];
		}
	}
	for (int i = 1; i <= N; i++) {
		for (int j = i; j <= N; j += i) {
			divisors[j].push_back(i);
		}
	}
}

int main() {
	sieve(maxn - 1);
	int T;
	scanf("%d", &T);
	while (T--) {
		unordered_map<int, int> times;
		unordered_map<int, ll> ans;
		int N, M, K;
		scanf("%d%d%d", &N, &M, &K);
		for (int i = 1; i <= N; i++) {
			scanf("%d", &a[i]);
			for (auto p : divisors[a[i]]) {
				times[p]++;
			}
		}
		while (K--) {
			int x;
			scanf("%d", &x);
			if (ans.count(x)) {
				printf("%lld\n", ans[x]);
				continue;
			}
			for (int i = 1; i * x <= M; i++) {
				ll m = times[i * x];
				ans[x] += (ll)mu[i] * m * m;
			}
			printf("%lld\n", ans[x]);
		}
	}
	return 0;
}

7. A. A Simple Math Problem

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q4mhmOdb-1689501390167)(数学专题训练3 数论.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1ODEyNzEx,size_16,color_FFFFFF,t_70)]

  • 莫比乌斯反演,一个很重要的作用是交换枚举次序。
  • 先放一个大雪菜在莫比乌斯反演部分推到过的一个公式,防止遗忘

s ( n ) = ∑ d ∣ n μ ( d ) = { 1 , n = 1 0 , n > 1 . s(n) = \sum\limits_{d|n}\mu(d) = \begin{cases}1,n=1\\0, n>1 \end{cases}. s(n)=dnμ(d)={1,n=10,n>1.

  • 推导是这样的

∑ j = 1 n ∑ i = j n F ( j ) [ g c d ( i , j ) = = 1 ] = ∑ j = 1 n F ( j ) ∑ i = j n ∑ d ∣ ( i , j ) μ ( d ) = ∑ j = 1 n ∑ i = j n ∑ d ∣ ( i , j ) F ( j ) μ ( d ) \sum\limits_{j=1}^n\sum_{i=j}^n F(j)[gcd(i, j) == 1] \\ =\sum\limits_{j=1}^nF(j)\sum\limits_{i = j}^{n}\sum\limits_{d|(i,j)}\mu(d) \\ =\sum\limits_{j=1}^{n}\sum\limits_{i=j}^n\sum\limits_{d|(i,j)}F(j)\mu(d) j=1ni=jnF(j)[gcd(i,j)==1]=j=1nF(j)i=jnd(i,j)μ(d)=j=1ni=jnd(i,j)F(j)μ(d)

  • 我们尝试交换枚举次序,枚举 d d d,用于 d d d 的含义是整除 g c d ( i , j ) gcd(i,j) gcd(i,j),因此d一定整除i,且d一定整除j. 因此,我们只需要枚举d的倍数即可

原式 = ∑ d = 1 n μ ( d ) ∑ j ′ = 1 ⌊ n / d ⌋ F ( d ∗ j ′ ) ∑ i ′ = j ′ ⌊ n / d ⌋ 1 = ∑ d = 1 n μ ( d ) ∑ j ′ = 1 ⌊ n / d ⌋ F ( d ∗ j ′ ) ( ⌊ n / d ⌋ − j ′ + 1 ) . 原式 = \sum\limits_{d=1}^n\mu(d)\sum\limits_{j'=1}^{\lfloor n/d \rfloor}F(d*j') \sum\limits_{i' = j'}^{\lfloor n/d \rfloor} 1 \\ = \sum\limits_{d=1}^n\mu(d)\sum\limits_{j'=1}^{\lfloor n/d \rfloor}F(d*j') (\lfloor n/d \rfloor - j' + 1). 原式=d=1nμ(d)j=1n/dF(dj)i=jn/d1=d=1nμ(d)j=1n/dF(dj)(⌊n/dj+1).

  • 预处理出来莫比乌斯函数、 F F F 函数就可以了。时间复杂度是 O ( n log ⁡ n ) O(n\log n) O(nlogn)​.
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long ll;

const int maxn = 100010;
int prime[maxn], cnt, mu[maxn], F[maxn];
bool st[maxn];

void sieve(int N)
{
    mu[1] = 1;
    for(int i = 2; i <= N; i++){
        if(!st[i]) prime[cnt++] = i, mu[i] = -1;
        for(int j = 0; prime[j] <= N / i; j++){
            st[i * prime[j]] = true;
            if(i % prime[j] == 0) break;
            mu[i * prime[j]] = -mu[i];
        }
    }
    for(int i = 0; i <= N; i++){
        int x = i;
        while(x){
            F[i] += x % 10;
            x /= 10;
        }
    }
}
int main()
{
    sieve(maxn - 1);
    int N;
    scanf("%d", &N);
    ll ans = 0;
    for(int d = 1; d <= N; d++){
        if(!mu[d]) continue;
        ll res = 0;
        for(int j = 1; j <= N / d; j++){
            res += (ll)F[j * d] * (N / d - j + 1LL);
        }
        ans += mu[d] * res;
    }
    printf("%lld\n", ans);
    return 0;
}

8. HDU 4549 M斐波那契数列 (费马小定理降幂&矩阵快速幂)

给一个新数列
KaTeX parse error: Expected 'EOF', got '&' at position 54: …1) * f(n - 2), &̲ n \ge 2
f ( n ) f(n) f(n),多组数据, 0 ≤ a , b , n ≤ 1 0 9 0 \le a, b, n \le 10^9 0a,b,n109

不难发现, f ( n ) = b F i b ( n ) a F i b ( n − 1 ) f(n) = b^{Fib(n)}a^{Fib(n-1)} f(n)=bFib(n)aFib(n1). 但是用费马小定理降幂的时候一定要小心 a x ≡ a x ( m o d p − 1 ) ( m o d p ) a^x \equiv a^{x \pmod {p-1}} \pmod p axax(modp1)(modp)

#include<bits/stdc++.h>
using namespace std;
const int N = 2;
typedef long long ll;
const ll mod = 1e9 + 7;

ll mod_pow(ll x, ll n)
{
    ll res = 1;
    while(n)
    {
        if(n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

void mul(ll c[], ll a[][N], ll b[])
{
    static ll tmp[N];
    memset(tmp, 0, sizeof tmp);
    for(int i = 0; i < 2; i++)
    {
        for(int j = 0; j < 2; j++)
        {
            tmp[i] = (tmp[i] + a[i][j] * b[j]) % (mod - 1);
        }
    }
    memcpy(c, tmp, sizeof tmp);
}

void mul(ll c[][N], ll a[][N], ll b[][N])
{
    static ll tmp[N][N];
    memset(tmp, 0, sizeof tmp);
    for(int i = 0; i < 2; i++)
    {
        for(int j = 0; j < 2; j++)
        {
            for(int k = 0; k < 2; k++)
            {
                tmp[i][j] = (tmp[i][j] + a[i][k] * b[k][j]) % (mod - 1);
            }
        }
    }
    memcpy(c, tmp, sizeof tmp);
}
int main()
{
    ll a, b, n;
    while(cin >> a >> b >> n)
    {
        if(n == 0)
        {
            printf("%lld\n", a);
            continue;
        }
        n--;
        ll fib[2] = {0, 1};
        ll f[][2] = {{0, 1}, {1, 1}};
        while(n)
        {
            if(n & 1) mul(fib, f, fib);
            mul(f, f, f);
            n >>= 1;
        }
        //printf("%lld\n", fib[1]);

        ll res = mod_pow(b, fib[1]) * mod_pow(a, fib[0]) % mod;
        printf("%lld\n", res);
    }
    return 0;
}

9. HDU 4196 Remoteland

题意: 在 1 ∼ n 1 \sim n 1n 中挑选不同的数字,使得它们的乘积是完全平方数,并且他们的乘积最大。求这个乘积模 1 e 9 + 7 1e9+7 1e9+7​.

其实就是把这些数字全部乘起来,然后统计一下每个质数出现多少次,如果出现奇数次,在把乘积除以这个质数。

也是完全平方数

Problem - 1497E1 - Codeforces

在这里插入图片描述

  • 这个题想复杂了。我们把所有的数字的质因数的幂都模2,这样子的话,我们从前往后遍离数组,并用一个map记录之前哪些数字出现过。
  • 如果新加进来的改变的数字并不存在于 map 中,说明当前数字加入当前区间不会引起冲突,于是把这个数字加入当前的区间之中。
  • 如果之前这个数出新过,那么就在这个数前面画一条分界线,把map清空,把这个数加到map里面,然后答案加1.
#include<bits/stdc++.h>
using namespace std;
const int N = 10000010;
int prime[N], cnt, st[N];
void sieve(int n)
{
    for(int i = 2; i <= n; i++){
        if(!st[i]) prime[cnt++] = st[i] = i;
        for(int j = 0; prime[j] <= n / i; j++){
            st[i * prime[j]] = prime[j];
            if(i % prime[j] == 0) break;
        }
    }
}
int change(int x)
{
    int res = 1;
    while(x > 1){
        int u = st[x];
        int cnt = 0;
        while(x % u == 0){
            cnt++;
            x /= u;
        }
        if(cnt & 1) res *= u;
    }
    return res;
}
int main()
{
    sieve(N - 1);
    int T;
    scanf("%d", &T);

    while(T--){
        set<int> S;
        int n, k;
        scanf("%d%d", &n, &k);
        int ans = 1, tmp;
        for(int i = 1; i <= n; i++){
            scanf("%d", &tmp);
            tmp = change(tmp);
            if(S.count(tmp)){
                ans++;
                S.clear();
            }
            S.insert(tmp);
        }
        printf("%d\n", ans);
    }
    return 0;
}

Problem - 1497E2 - Codeforces

  • 给一个序列 n ≤ 2 e 5 n \le 2e5 n2e5,问最少分割成几个连续字段,使得每个连续字段中不存在两个数的积是完全平方数. 可以改变中间的某个数字至多 k ( k ≤ 20 ) k(k \le 20) k(k20) 次,改为任意的数字即可.
  • 我们先处理出 L e f t ( i , j ) Left(i,j) Left(i,j) 表示从 i i i 开始,可以删掉 j j j 个数字,满足 [ l , i ] [l,i] [l,i] 中间是合法连续子序列的最往左的左区间端点是多少. 这个可以用双指针在 O ( n k ) O(nk) O(nk) 的时间内解决.
  • 然后令 f ( i , j ) f(i,j) f(i,j) 表示前 i i i 个数字,改变 j j j 个数字的最少划分数是多少. 这个我们可以枚举 x ∈ [ 0 , j ] x \in [0,j] x[0,j] l = L e f t ( i , j ) l = Left(i,j) l=Left(i,j),可以分为从两块,及 [ 1 , l − 1 ] [1, l - 1] [1,l1] 的最优划分加上 [ l , i ] [l,i] [l,i] 作为一个完整连续子段,并且前者改变 j − x j - x jx 次,后者改变 x x x 次.

f ( i , j ) = min ⁡ { f ( i , j ) , f ( l − 1 , j ) + 1 } f(i,j) = \min\{f(i,j),f(l-1,j) + 1\} f(i,j)=min{f(i,j),f(l1,j)+1}

  • 初始化的时候, f f f初始化为正无穷, f ( 0 , 0 ) f(0,0) f(0,0) 初始化为1即可.
#include<bits/stdc++.h>
using namespace std;
const int N = 200010, M = 10000010;
int prime[M], st[M], cnt;
void sieve(int n)
{
    for(int i = 2; i <= n; i++){
        if(!st[i]) prime[cnt++] = st[i] = i;
        for(int j = 0; prime[j] <= n / i; j++){
            st[i * prime[j]] = prime[j];
            if(i % prime[j] == 0) break;
        }
    }
}
int change(int x)
{
    vector<int> res;
    while(x > 1){
        int u = st[x];
        int cnt = 0;
        while(x % u == 0){
            x /= u;
            cnt++;
        }
        if(cnt & 1) res.push_back(u);
    }
    int ans = 1;
    for(auto p : res){
        ans = ans * p;
    }
    return ans;
}
int a[N], n, k, Left[N][30], f[N][30];
unordered_map<int, int> Map(N);
int main()
{
    sieve(M - 1);
    int T;
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; i++){
            int x;
            scanf("%d", &x);
            a[i] = change(x);
        }
        for(int i = 1; i <= n; i++){
            for(int j = 0; j <= k; j++){
                Left[i][j] = 0;
                f[i][j] = 1e9;
            }
        }
        f[0][0] = 0;
        for(int lim = 0; lim <= k; lim++){
            Map.clear();
            int cnt = 0;
            for(int i = 1, j = 1; i <= n; i++){
                Map[a[i]]++;
                if(Map[a[i]] > 1) cnt++;
                while(i >= j && cnt > lim){
                    if(Map[a[j]] > 1){
                        cnt--;
                    }
                    Map[a[j]]--;
                    j++;
                }
                Left[i][lim] = j;
            }
        }

        for(int i = 1; i <= n; i++){
            for(int j = 0; j <= k; j++){
                for(int x = 0; x <= j; x++){
                    int l = Left[i][x];
                    f[i][j] = min(f[i][j], f[l - 1][j - x] + 1);
                }
            }
        }
        int ans = 1e9;
        for(int j = 0; j <= k; j++){
            ans = min(ans, f[n][j]);
        }
        printf("%d\n", ans);
    }
    return 0;
}

HDU 3221 Brute-force Algorithm (矩阵 欧拉定理降幂)

广义欧拉定理

FZU 1759 Super A^B mod C

HDU 2837 Calculation

The Boss on Mars

给出正整数 n n n​,满足 1 ≤ n ≤ 1 0 8 1 ≤ n ≤ 10^8 1n108​,求 ∑ i = 1 n [ ( n , i ) = 1 ] i 4 m o d    ( 1 0 9 + 7 ) \sum\limits_{i=1}^{n} [(n,i)=1] i^4 \mod (10^9 + 7) i=1n[(n,i)=1]i4mod(109+7)​。

Mod Tree

给出方程 A x ≡ B ( m o d C ) A^x \equiv B \pmod C AxB(modC)​​,求 x x x​​ 的最小非负整数解. 1 ≤ A , B , C ≤ 1 0 9 1 \le A,B,C \le 10^9 1A,B,C109​​

数论之神

给出方程 X A ≡ B ( m o d C ) X^A \equiv B \pmod C XAB(modC),求 x x x [ 0 , C ) [0,C) [0,C) 上的整数解的个数.

1 ≤ A , B , C ≤ 1 0 9 1\le A, B, C \le 10^9 1A,B,C109,且 C C C 是奇数.

Interesting Yang Hui Triangle

给出非负整数 n n n 和素数 P P P,统计 C x n C_x^n Cxn 中有多少个不是 P P P 的倍数

n ≤ 1 0 9 , P < 1000 n \le 10^9, P < 1000 n109,P<1000

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值