HDU 5528 Count a - b 欧拉函数 素数

原题见HDU 5528

题意: f(m) 表示 abmodm0 (a,b) 的个数, a,b 为小于m的非负整数。
G(n)=m|nf(m)
给出 n(1n109) ,求 G(n)mod264 .

分析

mod264

最后结果要 mod 264 ,故结果用unsigned long long(64 bit)存,只要计算过程中只有加减乘,即是 mod 264 的结果。

f(m)

由于 0mmodm ,故
{ a,b 为小于m的非负整数得到的结果} = {a,b为1到m的正整数得到的结果}
对于 a ,有g=gcd(a,m),则第a行满足 abmodm=0 b=mg,2mg,...,gmg.g 。而对于1到m的a,满足 g=gcd(a,m)aϕ(mg) 个。

f(m)=m2gmgϕ(mg)

G(n)

G(n)=m|nf(m)=m|n(m2gmgϕ(mg))
=m|nm2m|ngmgϕ(mg)
X(n)=m|nm2,    Y(n)=m|ngmgϕ(mg)

X(n) 即所有n的约数的平方和。若对n作质因数分解
n=pa11pa22...pass
X(n)=(p01+p21+...+p2a11)(p02+p22+...+p2a22)...(p0s+p2s...+p2ass)
=Π1is0jaip2ji

注意求和部分不能用等比公式求出通项。因为这样会产生除法,如果分子在运算时已经超 264 会造成无法整除等运算错误。

对于 Y(n) ,若给定n和m,则会产生数对 (g,mg) .且一个数对只对应唯一的m,可以得到所有的数对 (a,b) 满足 abn ,故

Y(n)=xynxϕ(y)=xn(xynxϕ(y))
已知 欧拉函数性质
dnϕ(d)=n
Y(n)=xn(xnx)=nxn1=nΠ1is(ai+1)
G(n)=Π1is0jaip2jinΠ1is(ai+1)

附代码

注意:要估算好素数表的大小避免超时
sum[i][j] =0kjp2ki
p[i]=素数在表中时保存序号,在表外时直接保存素数

/*--------------------------------------------
 * File Name: HDU 5528
 * Author: Danliwoo
 * Mail: Danliwoo@outlook.com
 * Created Time: 2016-07-10 14:05:24
--------------------------------------------*/

#include <bits/stdc++.h>
using namespace std;
#define LL unsigned long long
#define N 32000
int num[N], prim[N], pn;
#define M 32
LL sum[N][M], p[M];
int a[M];
void st(){
    pn = 0;
    memset(num, -1, sizeof(num));
    for(int i = 2;i < N;i++){
        if(num[i]) prim[pn++] = i;
        for(int j = 0;j < pn && 1LL*i*prim[j] < N;j++){
            num[i*prim[j]] = 0;
            if(i % prim[j] == 0) break;
        }
    }
    memset(sum, 0, sizeof(sum));
    for(int i = 0;i < pn;i++){
        sum[i][0] = 1;
        LL pm = 1LL*prim[i] * prim[i];
        for(int j = 1;j < M;j++)
            sum[i][j] = sum[i][j-1] * pm;
        for(int j = 1;j < M;j++)
            sum[i][j] += sum[i][j-1];
    }
}
int main()
{
    st();
    int T;
    scanf("%d", &T);
    while(T--){
        int n;
        scanf("%d", &n);
        memset(p, 0, sizeof(p));
        memset(a, 0, sizeof(a));
        int m = n, s = 0;
        for(int i = 0;i < pn;i++) if(m % prim[i] == 0){
            p[s++] = i;
            while(m % prim[i] == 0){
                a[s-1]++;
                m /= prim[i];
            }
            if(m == 1) break;   //大素数表的时候这个break很有用
        }
        if(m > 1){
            p[s++] = m;
            a[s-1] = 1;
        }
        LL x = 1, y = 1;
        for(int i = 0;i < s;i++){
            if(p[i] < pn)
                x *= sum[p[i]][a[i]];
            else 
                x *= (1+p[i]*p[i]);
        }
        for(int i = 0;i < s;i++)
            y *= 1LL*(a[i]+1);
        y *= 1LL*n;
        printf("%I64u\n", x-y);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值