洛谷-P1829 Crash的数字表格--积性函数

推式子

a n s = ∑ i = 1 N ∑ j = 1 M l c m ( i , j ) = ∑ i = 1 N ∑ j = 1 M i ∗ j gcd ⁡ ( i , j ) \begin{aligned} ans = \sum_{i=1}^{N}\sum_{j=1}^{M}lcm(i,j) = \sum_{i=1}^{N}\sum_{j=1}^{M}\frac{i*j}{\gcd(i,j)} \end{aligned} ans=i=1Nj=1Mlcm(i,j)=i=1Nj=1Mgcd(i,j)ij
很明显出现了 gcd ⁡ \gcd gcd,我们枚举 gcd ⁡ ( i , j ) = d \gcd(i,j)=d gcd(i,j)=d
a n s = ∑ d = 1 min ⁡ ( N , M ) ∑ i = 1 N ∑ j = 1 M i ∗ j d [ gcd ⁡ ( i , j ) = = d ] \begin{aligned} ans = \sum_{d=1}^{\min(N,M)}\sum_{i=1}^{N}\sum_{j=1}^{M}\frac{i*j}{d}[\gcd(i,j)==d] \end{aligned} ans=d=1min(N,M)i=1Nj=1Mdij[gcd(i,j)==d]
a n s = ∑ d = 1 min ⁡ ( N , M ) ∑ i = 1 ⌊ N d ⌋ ∑ j = 1 ⌊ M d ⌋ d ∗ i ∗ j [ gcd ⁡ ( i , j ) = = 1 ] \begin{aligned} ans = \sum_{d=1}^{\min(N,M)}\sum_{i=1}^{\lfloor\frac{N}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{M}{d}\rfloor}d*i*j[\gcd(i,j)==1] \end{aligned} ans=d=1min(N,M)i=1dNj=1dMdij[gcd(i,j)==1]
接下来我们有很多方法去化简这个式子,我使用 ∑ x ∣ g c d ( i , j ) μ ( x ) \sum_{x \mid gcd(i,j)}\mu(x) xgcd(i,j)μ(x)替换 [ gcd ⁡ ( i , j ) = = 1 ] [\gcd(i,j) == 1] [gcd(i,j)==1]
a n s = ∑ d = 1 min ⁡ ( N , M ) ∑ i = 1 ⌊ N d ⌋ ∑ j = 1 ⌊ M d ⌋ d ∗ i ∗ j ∑ x ∣ g c d ( i , j ) μ ( x ) \begin{aligned} ans = \sum_{d=1}^{\min(N,M)}\sum_{i=1}^{\lfloor\frac{N}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{M}{d}\rfloor}d*i*j\sum_{x \mid gcd(i,j)}\mu(x) \end{aligned} ans=d=1min(N,M)i=1dNj=1dMdijxgcd(i,j)μ(x)
然后我们枚举x
a n s = ∑ d = 1 min ⁡ ( N , M ) d ∑ x = 1 min ⁡ ( N d , M d ) μ ( x ) ∑ x i = 1 ⌊ N d ⌋ ∑ x j = 1 ⌊ M d ⌋ x 2 ∗ i ∗ j \begin{aligned} ans = \sum_{d=1}^{\min(N,M)}d\sum_{x=1}^{\min(\frac{N}{d},\frac{M}{d})}\mu(x)\sum_{xi=1}^{\lfloor\frac{N}{d}\rfloor}\sum_{xj=1}^{\lfloor\frac{M}{d}\rfloor}x^{2}*i*j \end{aligned} ans=d=1min(N,M)dx=1min(dN,dM)μ(x)xi=1dNxj=1dMx2ij
a n s = ∑ d = 1 min ⁡ ( N , M ) d ∑ x = 1 min ⁡ ( N d , M d ) μ ( x ) ∗ x 2 ∑ i = 1 ⌊ N x d ⌋ ∑ j = 1 ⌊ M x d ⌋ i ∗ j \begin{aligned} ans = \sum_{d=1}^{\min(N,M)}d\sum_{x=1}^{\min(\frac{N}{d},\frac{M}{d})}\mu(x)*x^{2}\sum_{i=1}^{\lfloor\frac{N}{xd}\rfloor}\sum_{j=1}^{\lfloor\frac{M}{xd}\rfloor}i*j \end{aligned} ans=d=1min(N,M)dx=1min(dN,dM)μ(x)x2i=1xdNj=1xdMij
推到这里就可以写了,两次分块时间复杂度接近O(n)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
void out(T x) { cout << x << endl; }
const int N = 1e7 + 6;
const ll mod = 20101009;
const ll inv2 = 10050505;
ll fast_pow(ll a, ll b, ll p) {ll c = 1; while(b) { if(b & 1) c = c * a % p; a = a * a % p; b >>= 1;} return c;}
int mu[N], prime[N], tot;
ll sum[N];
bool mark[N];
void get_mu()
{
    tot = 0;
    mark[0] = mark[1] = true;
    mu[1] = 1;
    for(int i = 2; i < N; i ++)
    {
        if(!mark[i])
        {
            prime[tot ++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < tot && i * prime[j] < N; j ++)
        {
            mark[i * prime[j]] = true;
            if(i % prime[j] == 0)
                break;
            mu[i * prime[j]] = -mu[i];
        }
    }
    for(int i = 1; i < N; i ++)
        sum[i] = (sum[i - 1] + mu[i] * i % mod * i % mod) % mod;
}
int main()
{
    ios::sync_with_stdio(false);
    get_mu();
    ll n, m;
    cin >> n >> m;
    if(n > m)
        swap(n, m);
    ll ans = 0;
    for(ll d = 1, t = 1; d <= n; d = t + 1)
    {
        t = min(n / (n / d), m / (m / d));
        ll bns = 0, dn = n / d, dm = m / d;
        for(ll i = 1, j = 1; i <= dn; i = j + 1)
        {
            j = min(dn / (dn / i), dm / (dm / i));
            bns = (bns + (sum[j] - sum[i - 1]) * (dn / i) % mod * (dn / i + 1ll) % mod * inv2 % mod * (dm / i) % mod * (dm / i  + 1ll) % mod * inv2 % mod + mod) % mod;
        } 
        ans = (ans + (t - d + 1) * (t + d) % mod * inv2 % mod * bns % mod + mod) % mod;
    }
    cout << ans << endl;
}

然后就这样我们还可以继续化简
我们令 T = x d T=xd T=xd,令 f ( x ) = ∑ i = 1 x i f(x)=\sum_{i=1}^{x}i f(x)=i=1xi
a n s = ∑ d = 1 min ⁡ ( N , M ) d ∑ x = 1 min ⁡ ( N d , M d ) μ ( x ) ∗ x 2 f ( ⌊ N T ⌋ ) f ( ⌊ M T ⌋ ) \begin{aligned} ans = \sum_{d=1}^{\min(N,M)}d\sum_{x=1}^{\min(\frac{N}{d},\frac{M}{d})}\mu(x)*x^{2}f(\lfloor\frac{N}{T}\rfloor)f(\lfloor\frac{M}{T}\rfloor) \end{aligned} ans=d=1min(N,M)dx=1min(dN,dM)μ(x)x2f(TN)f(TM)
然后我们枚举T
a n s = ∑ T = 1 min ⁡ ( N , M ) f ( ⌊ N T ⌋ ) f ( ⌊ M T ⌋ ) ∑ d ∣ T d ∗ μ ( T d ) ∗ ( T d ) 2 \begin{aligned} ans = \sum_{T=1}^{\min(N,M)}f(\lfloor\frac{N}{T}\rfloor)f(\lfloor\frac{M}{T}\rfloor)\sum_{d \mid T}d*\mu(\frac{T}{d})*(\frac{T}{d})^{2} \end{aligned} ans=T=1min(N,M)f(TN)f(TM)dTdμ(dT)(dT)2
a n s = ∑ T = 1 min ⁡ ( N , M ) f ( ⌊ N T ⌋ ) f ( ⌊ M T ⌋ ) ∑ d ∣ T μ ( T d ) ∗ ( T d ) ∗ T \begin{aligned} ans = \sum_{T=1}^{\min(N,M)}f(\lfloor\frac{N}{T}\rfloor)f(\lfloor\frac{M}{T}\rfloor)\sum_{d \mid T}\mu(\frac{T}{d})*(\frac{T}{d})*T \end{aligned} ans=T=1min(N,M)f(TN)f(TM)dTμ(dT)(dT)T
后面那个式子我们枚举 T d \frac{T}{d} dT,即另 d = T d d=\frac{T}{d} d=dT
a n s = ∑ T = 1 min ⁡ ( N , M ) f ( ⌊ N T ⌋ ) f ( ⌊ M T ⌋ ) ∑ d ∣ T d ∗ μ ( d ) ∗ T \begin{aligned} ans = \sum_{T=1}^{\min(N,M)}f(\lfloor\frac{N}{T}\rfloor)f(\lfloor\frac{M}{T}\rfloor)\sum_{d \mid T}d*\mu(d)*T \end{aligned} ans=T=1min(N,M)f(TN)f(TM)dTdμ(d)T
这样似乎并没有改变什么复杂度,但是我们发现后面的 ∑ d ∣ T d ∗ μ ( d ) 是 个 积 性 函 数 \sum_{d \mid T}d*\mu(d)是个积性函数 dTdμ(d)
我们分析一下
h ( T ) = ∑ d ∣ T d ∗ μ ( d ) h(T)=\sum_{d \mid T}d*\mu(d) h(T)=dTdμ(d)
T ∈ p r i m e T \in prime Tprime, h ( T ) = 1 − T h(T)=1-T h(T)=1T
现有 x = i ∗ p r i m e [ j ] x=i*prime[j] x=iprime[j]且i中无prime[j]这个质因子
h ( i ∗ p r i m e [ j ] ) = ∑ d ∣ ( i ∗ p r i m e [ j ] ) d ∗ μ ( d ) h(i*prime[j])=\sum_{d \mid (i * prime[j])}d*\mu(d) h(iprime[j])=d(iprime[j])dμ(d)
我们发现多了个质数会在原有约数的基础上翻一倍,并且一一对应
设z是i的约数,我们有 z ∗ p r i m e [ j ] ∗ μ ( z ∗ p r i m e [ j ] ) = − z ∗ μ ( z ) ∗ p r i m e [ j ] z*prime[j]*\mu(z*prime[j])=-z*\mu(z)*prime[j] zprime[j]μ(zprime[j])=zμ(z)prime[j]
所以这一对的贡献就是 ( 1 − p r i m e [ j ] ) ∗ z ∗ μ ( z ) (1-prime[j])*z*\mu(z) (1prime[j])zμ(z)
对于每一对我们都可以提出个 ( 1 − p r i m e [ j ] ) (1-prime[j]) (1prime[j])
h ( i ∗ p r i m e [ j ] ) = h ( i ) ∗ ( 1 − p r i m e [ j ] ) = h ( i ) ∗ h ( p r i m e [ j ] ) h(i*prime[j])=h(i)*(1-prime[j])=h(i)*h(prime[j]) h(iprime[j])=h(i)(1prime[j])=h(i)h(prime[j])
发现这玩意是个积性函数
如果 i i i中存在 p r i m e [ j ] prime[j] prime[j]这个质因子
h ( i ∗ p r i m e [ j ] ) = ∑ d ∣ ( i ∗ p r i m e [ j ] ) d ∗ μ ( d ) h(i*prime[j])=\sum_{d \mid (i * prime[j])}d*\mu(d) h(iprime[j])=d(iprime[j])dμ(d)
对于d
如果d中有prime[j]这个质因子,我们会发现 μ ( d ) = 0 \mu(d)=0 μ(d)=0因为存在平方因子
如果d中无prime[j]这个质因子,我们会发现是 h ( i ) 的 组 成 h(i)的组成 h(i)
最后 h ( i ∗ p r i m e [ j ] ) = h ( i ) h(i*prime[j])=h(i) h(iprime[j])=h(i)

这样我们可以用线性筛求出 h ( T ) h(T) h(T)啦,那个T在求前缀和的时候乘上
那前面直接分块,时间复杂度降低到O( n \sqrt{n} n )

筛法代码:

int gu[N], prime[N], tot;//gu为积性函数
bool mark[N];
void get_gu()
{
    tot = 0;
    mark[0] = mark[1] = true;
    gu[1] = 1;
    for(int i = 2; i < N; i ++)
    {
        if(!mark[i])
        {
            gu[i] = 1 - i;
            prime[tot ++] = i;
        }
        for(int j = 0; j < tot && i * prime[j] < N; j ++)
        {
            mark[i * prime[j]] = true;
            if(i % prime[j] == 0)
            {
                gu[i * prime[j]] = gu[i];
                break;
            }
            gu[i * prime[j]] = gu[i] * (1 - prime[j]);
        }
    }
}

神仙的是我应该O( n \sqrt{n} n )代码居然没我O(n)跑的快

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值