Enzymii的hu测 T3.violetevergarden(反演【约数个数)

版权属于Enzymii,想要引用此题(包括题面)的朋友请联系博主

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

分析:

题目来源

首先要好好读题,题目有一点问题
不过不要在意这么多细节,实际上题目要求:

i=1nd(i2) ∑ i = 1 n d ( i 2 )

d的暴力计算公式
n=pkii n = ∏ p i k i
d(n)=(ki+1) d ( n ) = ∑ ( k i + 1 )

ZYXZYXZYX和我说有一个60%的做法,简单反演一下就可以了:

首先,关于 d d 函数(约束个数)有一个玄学公式

d(nm)=i|nj|m[gcd(i,j)=1]

k=1nd(k2)=k=1ni|kj|k[gcd(i,j)=1] ∑ k = 1 n d ( k 2 ) = ∑ k = 1 n ∑ i | k ∑ j | k [ g c d ( i , j ) = 1 ]

=k=1ni|kj|kd|gcd(i,j)μ(d) = ∑ k = 1 n ∑ i | k ∑ j | k ∑ d | g c d ( i , j ) μ ( d )

=k=1ni|kj|kd=1kd|id|jμ(d) = ∑ k = 1 n ∑ i | k ∑ j | k ∑ d = 1 k ∑ d | i ∑ d | j μ ( d )

=k=1nd=1kμ(d)i|kj|kd|id|j1 = ∑ k = 1 n ∑ d = 1 k μ ( d ) ∑ i | k ∑ j | k ∑ d | i ∑ d | j 1

=k=1nd=1kμ(d)i|kd|ij|kd|j1 = ∑ k = 1 n ∑ d = 1 k μ ( d ) ∑ i | k ∑ d | i ∑ j | k ∑ d | j 1

显然 i|kd|ij|kd|j1 ∑ i | k ∑ d | i ∑ j | k ∑ d | j 1 就是 μ(d) μ ( d ) 的计算次数
而通过观察我们可以发现 i|kd|i ∑ i | k ∑ d | i j|kd|j ∑ j | k ∑ d | j 实际上是等价的

下面我们考虑如何计算 i|kd|i1 ∑ i | k ∑ d | i 1
上式的意义:i是k的约数,d是i的约数
i=pd i = p d ,则有

pd|k1=p|kd1 ∑ p d | k 1 = ∑ p | k d 1

仔细观察上式,意义即为: kd k d 的约数个数和,即 d(kd) d ( k d )

因此原式可化为:

k=1nd=1kμ(d)d2(kd) ∑ k = 1 n ∑ d = 1 k μ ( d ) d 2 ( k d )

满分做法

我们要用到暴力计算d的公式
n=ki=1paii n = ∏ i = 1 k p i a i ,则 n2=ki=1p2aii n 2 = ∏ i = 1 k p i 2 a i

d(n)=i=1k(ai+1) d ( n ) = ∑ i = 1 k ( a i + 1 )

d(n2)=i=1k(2ai+1) d ( n 2 ) = ∑ i = 1 k ( 2 a i + 1 )

因为 n=ki=1paii n = ∏ i = 1 k p i a i ,那么 n n 的约数中pi的幂范围就是 0ai 0 − a i
因为 n2=ki=1p2aii n 2 = ∏ i = 1 k p i 2 a i ,那么 n2 n 2 的约数中 pi p i 的幂范围就是 02ai 0 − 2 a i
n n 的约数一定是n2的约数,那么我们考虑 n2 n 2 的约数 X X ,但是X不是 n n 的约数
那么X一定可以表示为

X=npaii X = n 的 某 个 约 数 ∗ ∏ p i a i

意义即为:X比n的某个约数多乘上若干个 paii p i a i
那么到底是多乘上了那些 paii p i a i 呢?我们可以枚举

n n 的不同约数有ω(n)
那么这 ω(n) ω ( n ) 个元素能组成的集合个数:

2ω(n) 2 ω ( n )

对于上面的 2ω(n) 2 ω ( n ) 集合,我们都可以让ta与 n n 的任何一个约数重组出n2的合法约数

举个例子:
n=pa11pa22 n = p 1 a 1 p 2 a 2
n2=p2a11p2a22 n 2 = p 1 2 a 1 p 2 2 a 2
ω(n)=2 ω ( n ) = 2
那么我们能得到的集合为 {{0,0},{0,1},{1,0},{1,1}} { { 0 , 0 } , { 0 , 1 } , { 1 , 0 } , { 1 , 1 } }
以n的一个约数 p11p12 p 1 1 p 2 1 为基准,上面的集合与约数 p11p12 p 1 1 p 2 1 能组成的 n2 n 2 的约数分别为:
p11p12,p11pa2+12,pa1+11p12pa1+11pa2+12 p 1 1 p 2 1 , p 1 1 p 2 a 2 + 1 , p 1 a 1 + 1 p 2 1 , p 1 a 1 + 1 p 2 a 2 + 1

考虑如何快速的计算 2ω(n) 2 ω ( n )
观察这些集合的特点,集合元素非0即1

举个例子:
n=pa11pa22pa33 n = p 1 a 1 p 2 a 2 p 3 a 3
ω(n)=2 ω ( n ) = 2
一个集合为 {0,1,1} { 0 , 1 , 1 }
显然 {0,1,1} { 0 , 1 , 1 } 分别对应着 p1,p2,p3 p 1 , p 2 , p 3 是否选择了
如果我们干脆把 {0,1,1} { 0 , 1 , 1 } 视为 p1,p2,p3 p 1 , p 2 , p 3 的指数
得到一个数 p01+p02+p3 p 1 0 + p 2 0 + p 3

我们用上述方法得到的的数每个质因子的指数非0即1,也就是说没有平方因子
一提到没有平方因子,就可以想到 μ μ 函数,实际上:

2ω(n)=d|nμ2(d)

意义: n n 的没有平方因子(μ!=0)的约数的计数

回到我们一开始的问题:

d(n2)=i=1k(2ai+1) d ( n 2 ) = ∑ i = 1 k ( 2 a i + 1 )

之前我们说过:对于 2ω(n) 2 ω ( n ) 个集合,我们都可以让ta与 n n 的任何一个约数重组出n2的合法约数

d(n2)=i=1k(2ai+1)=i|nd(i)2ω(n)=i|nd(i)j|nμ2(j) d ( n 2 ) = ∑ i = 1 k ( 2 a i + 1 ) = ∑ i | n d ( i ) ∗ 2 ω ( n ) = ∑ i | n d ( i ) ∑ j | n μ 2 ( j )

枚举 μ μ

i=1nμ2(i)j=1nid(j)

于是利用整除分块,我们只要知道 μ2(i) μ 2 ( i ) d(i) d ( i ) 的前缀和,预处理前 n23 n 2 3 项的情况下, O(n23) O ( n 2 3 ) 求出

前者就是 1n 1 − n 的无平方因子数个数,考虑容斥 O(n) O ( n )

i=1nμ2(i)=i=1nμ(i)ni2 ∑ i = 1 n μ 2 ( i ) = ∑ i = 1 n μ ( i ) ∗ n i 2

后者就是套路

i=1nd(i)=i=1nj=11ni1j[gcd(i,j)=1]=i=1nni ∑ i = 1 n d ( i ) = ∑ i = 1 n ∑ j = 1 1 n i 1 j [ g c d ( i , j ) = 1 ] = ∑ i = 1 n n i

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#define ll long long

using namespace std;

const int N=10000001;
ll n;
int sshu[N],tot=0;
ll mu[N],d[N],sm[N],sd[N],c[N];
bool no[N];

void prepare() {
    mu[1]=1; d[1]=1;
    for (int i=2;i<N;i++) {
        if (!no[i]) {
            sshu[++tot]=i;
            mu[i]=-1;
            d[i]=2; c[i]=1;
        }
        for (int j=1;j<=tot&&sshu[j]*i<N;j++) {
            int v=sshu[j]*i;
            no[v]=1; c[v]=1;
            if (i%sshu[j]==0) {
                c[v]=c[i]+1;
                mu[v]=0; d[v]=d[i]*(c[v]+1)/c[v];
                break;
            }
            mu[v]=-mu[i]; d[v]=d[i]<<1;
        }
    }

    for (int i=1;i<N;i++)
        sm[i]=sm[i-1]+mu[i]*mu[i],
        sd[i]=sd[i-1]+d[i];
}

ll D(ll x) {
    if (x<N) return (ll)sd[x];
    ll ans=0,last;
    for (ll i=1;i<=x;i=last+1) {
        last=x/(x/i);
        ans+=(x/i*(ll)(last-i+1));
    }
    return ans;
}

ll Mu(ll x) {
    if (x<N) return sm[x];
    ll ans=0,last;
    for (ll i=1;i*i<=x;i++) 
        ans+=(mu[i]*(n/(i*i)));
    return ans;
}

ll solve(ll n) {
    ll ans=0,last;
    for (ll i=1;i<=n;i=last+1) { 
        last=n/(n/i);
        ans+=D(n/i)*(Mu(last)-Mu(i-1));
    }
    return ans;
}

int main()
{
    int T;
    prepare();
    scanf("%d",&T);
    while (T--) {
        scanf("%lld",&n);
        printf("%lld\n",solve(n));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值