Regional 2015 - Asia Changchun - B Count a × b

题目:http://acm.nenu.edu.cn/regional-2015/files/2015/10/problemset_20151015.pdf

题意:
f(m) 表示满足 0a,b<m mab 的二元组 (a,b) 的个数。
g(n)=m|nf(m) ,询问 T 次某个 g(n) 的值,答案模 264
T20000,n109

题解:
(64位无符号整型变量的运算可以看做是在模 264 意义下的运算)
看到 g(n) 的形式很容易想到一个事实,如果 f(m) 是积性函数,即”对于互质的 a b ,有 f(ab)=f(a)f(b) ”,那么 g(n) 的表达式可以做到如下的改写:
n=pk11pk22pktt ,那么

g(n)=m|nf(m)=(f(1)+f(p1)+f(p21)++f(pk11))(f(1)+f(p2)+f(p22)++f(pk22))(f(1)+f(pt)+f(p2t)++f(pktt))=i=1tj=0kif(pji)

上面式子可以这样理解,对于每个 n 的因子 m m 对应的质因子的次幂一定小于等于 n 对应的质因子次幂,从上述乘积的每一个括号里选出一个数字,乘起来就对应一个 f(m) ,这样算是不重不漏的。
这样可以将 O(n) 枚举因子的过程转化为 O(logn) 枚举质因子的过程。
然而这个题里的 f(m) 并不积性,原因很容易想到,因为它的定义应该是一个积性函数的反面,那么我们来看看这个反面。
定义 h(m) 表示满足 0a,b<m m|ab 的二元组 (a,b) 的个数,那么显然就有 f(m)=m2h(m) ,其中 m2 显然是一个积性函数,而考虑计算 h(m) 的时候,不难发现 a b 里不同的质因子之间不会产生影响,可以分别把每种质因子的情况算出来之后利用乘法原理乘起来,这也就意味着 h(m) 是积性的,所以如果知道 h(pk) 怎么算,就可以利用上述的转化过程 O(logn) 求解。
先考虑 n 的所有因子 m 对应的 m2 之和,直接按照上式计算即可。
再来考虑 h(m) ,显然 a b 取值范围从 0a,b<m 变成 1a,bm 是不会改变答案的。
考虑 m=pk 的情况,令 a=ptara,b=ptbrb ,则 rapkta(ra,p)=1 rbpktb(rb,p)=1 。实际上,如果固定了 ta tb (a,b) 的个数是可以直接算出来的,即 ϕ(pkta)ϕ(pktb)
现在所求即
h(m)=h(pk)=ta=0ktb=0k[ta+tbk]ϕ(pkta)ϕ(pktb)=ta=0ktb=0k[ta+tbk]ϕ(pta)ϕ(ptb)=i=0kj=0iϕ(pj)ϕ(pkj)=1+i=1k(i+1)pi2ipi1+(i1)pi2=(k+1)pkkpk1

更进一步地,有 ki=0h(pi)=(k+1)pk
于是可以预处理出 O(n) 以内的质数,单次 O(logn) 计算答案。

代码:

#include <cstdio>
typedef unsigned long long ULL;
const int maxn = 32000;
int tot, prime[maxn], t, n;
bool vis[maxn];
ULL ans1, ans2;
int main()
{
    for(int i = 2; i < maxn; ++i)
    {
        if(!vis[i])
            prime[tot++] = i;
        for(int j = 0, o; j < tot && (o = i * prime[j]) < maxn; ++j)
        {
            vis[o] = 1;
            if(i % prime[j] == 0)
                break;
        }
    }
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        ans1 = ans2 = 1;
        for(int i = 0; i < tot && prime[i] * prime[i] <= n; ++i)
            if(n % prime[i] == 0)
            {
                int cnt = 0, tmp = 1;
                ULL sum = 1;
                for( ; n % prime[i] == 0; n /= prime[i], ++cnt);
                for(int j = 1; j <= cnt; ++j)
                {
                    tmp *= prime[i];
                    sum += (ULL)tmp * tmp;
                }
                ans1 *= sum;
                ans2 *= tmp * (cnt + 1ULL);
            }
        if(n > 1)
        {
            ans1 *= (ULL)n * n + 1;
            ans2 *= n * 2ULL;
        }
        printf("%llu\n", ans1 - ans2);
    }
    return 0;
}

然而会做这道题也不能拿金。

你可能感兴趣的:(数论,积性函数)