求最小公倍数c语言欧拉,51nod 1363 最小公倍数的和 欧拉函数+二进制枚举

Input

第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 50000)

第2 - T + 1行:T个数A[i](A[i] <= 10^9)

Output

共T行,输出对应的最小公倍数之和

Input示例

3

5

6

9

Output示例

55

66

279

思路:这题数据很大,还需要一些剪枝,不然过不去;

首先最小公倍数的和,不考虑时间复杂度,

求法:

首先你需要知道有个这样的定理:如果 gcd(n,i)=1则 gcd(n,n-i)=1 (1<=i<=n)

可得小于n的并与n互质的和为p*phi(p)/2;

p=1的时候是特例需要特判+1;

答案显然等于 n * (phi(g) * g / 2 + 1) g | n, g >= 1

显然需要枚举n的因数,最常见sqrt(n)的写法,根据题目T*sqrt(n)显然超时;

优化:

首先你需要知道欧拉函数的两个积性函数的性质:

欧拉函数是积性函数——若m,n互质,φ(mn)=φ(m)φ(n)。

若n是质数p的k次幂,φ(n)=p^k-p^(k-1)=(p-1)p^(k-1),因为除了p的倍数外,其他数都跟n互质。

然后将n质因数分解,二进制枚举,这时你会发现万一有多个相同的质数会重复累加,所以将存不同质数,和质数的个数;

在枚举的时候后面的指数跟前面的数不管如何乘必然互质,这就可以用到积性函数的第一个性质;

在处理相同质数,用到第二个质数;

如果p是质数,φ(p)=p-1;

#include

using namespace std;

#define ll long long

#define mod 1000000007

#define inf 999999999

#define esp 0.00000000001

ll scan()

{

ll res = , ch ;

while( !( ( ch = getchar() ) >= '' && ch <= '' ) )

{

if( ch == EOF ) return << ;

}

res = ch - '' ;

while( ( ch = getchar() ) >= '' && ch <= '' )

res = res * + ( ch - '' ) ;

return res ;

}

const int MAXN=;

int fa[];

int si[],p;

ll ans;

int prime[MAXN],cnt;

bool vis[MAXN];

inline int Prime(int n)

{

cnt=;

memset(vis,,sizeof(vis));

for(int i=;i

{

if(!vis[i])

prime[cnt++]=i;

for(int j=;j

{

vis[i*prime[j]]=;

if(i%prime[j]==)

break;

}

}

return cnt;

}

inline void dfs(ll fac,ll pos,ll x,ll oula)

{

if(pos==p)

{

ans+=oula*fac/;

ans%=mod;

return;

}

ll base=;

ll hh=;

for(int i=;i<=si[pos];i++)

{

dfs(fac*base,pos+,x,oula*hh);

base*=fa[pos];

hh*=fa[pos]-(i==?:);

}

}

int main()

{

ll x,y,z,i,t;

Prime(MAXN);

int T;

scanf("%d",&T);

while(T--)

{

memset(si,,sizeof(si));

scanf("%lld",&x);

ans=;

p=;

z=x;

for(i=;i

{

if(z%prime[i]==)

{

fa[p]=prime[i];

while(z%prime[i]==)

{

z/=prime[i];

si[p]++;

}

p++;

}

}

if(z>)

{

fa[p]=z;

si[p++]++;

}

dfs(,,x,);

printf("%lld\n",(x*(ans+))%mod);

}

return ;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值