HDU5528 Count a b(欧拉函数&数的分解)

HDU5528 Count a * b(欧拉函数&数的分解)

题目大意

f ( n ) f(n) f(n)为小于n的a,b使得有 a × b   m o d   n = ̸ 0 a\times b\ mod \ n =\not 0 a×b mod n≠0的a,b的组数。现定义 g ( n ) = ∑ d ∣ n f ( d ) g(n)=\sum_{d|n}f(d) g(n)=dnf(d),现给出n求g(n)

解题思路

f ( n ) = n ∗ n − ∑ i = 0 n − 1 ∑ j = 0 n − 1 n ∣ ( i ∗ j ) = n ∗ n − ∑ i = 0 n − 1 ∑ j = 0 n − 1 n g c d ( n , i ) ∣ ( i g c d ( n , i ) ∗ j ) = n ∗ n − ∑ i = 0 n − 1 ∑ j = 0 n − 1 n g c d ( n , i ) ∣ j = n ∗ n − ∑ i = 0 n − 1 g c d ( n , i ) f(n)=n*n-\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}n|(i*j)\\ =n*n-\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}{n\over gcd(n,i)}|({i\over gcd(n,i)}*j)\\ =n*n-\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}{n\over gcd(n,i)}|j\\ =n*n-\sum_{i=0}^{n-1}gcd(n,i) f(n)=nni=0n1j=0n1n(ij)=nni=0n1j=0n1gcd(n,i)n(gcd(n,i)ij)=nni=0n1j=0n1gcd(n,i)nj=nni=0n1gcd(n,i)

此时需要用到一个常用套路 ∑ i = 1 n g c d ( n , i ) = ∑ d ∣ n d φ ( n d ) \sum_{i=1}^{n}gcd(n,i)=\sum_{d|n}d\varphi({n\over d}) i=1ngcd(n,i)=dndφ(dn)

那么原式就可以化成
= n ∗ n − ∑ d ∣ n d φ ( n d ) =n*n-\sum_{d|n}d\varphi({n\over d}) =nndndφ(dn)
f ( n ) f(n) f(n)带入
g ( n ) = ∑ d ∣ n d 2 − ∑ d ∣ n ∑ i ∣ d i φ ( d i ) = ∑ d ∣ n d 2 − ∑ i ∣ n i ∑ i ∣ d & d ∣ n φ ( d i ) = ∑ d ∣ n d 2 − ∑ i ∣ n i ∑ k ∣ n i φ ( k ) g(n)=\sum_{d|n}d^2-\sum_{d|n}\sum_{i|d}i\varphi({d\over i})\\ =\sum_{d|n}d^2-\sum_{i|n}i\sum_{i|d\&d|n}\varphi({d\over i})\\ =\sum_{d|n}d^2-\sum_{i|n}i\sum_{k|{n\over i}}\varphi(k)\\ g(n)=dnd2dnidiφ(id)=dnd2iniid&dnφ(id)=dnd2inikinφ(k)
此时需要用到欧拉函数的一个性质
∑ d ∣ n φ ( d ) = n \sum_{d|n}\varphi(d)=n dnφ(d)=n
原式就可以换成
= ∑ d ∣ n d 2 − ∑ i ∣ n i n i = ∑ d ∣ n d 2 − ∑ i ∣ n n =\sum_{d|n}d^2-\sum_{i|n}i{n\over i}\\ =\sum_{d|n}d^2-\sum_{i|n}n =dnd2iniin=dnd2inn
接下来分解因数即可。本题时限卡得比较严格,分解时需要先处理出质数,再做质因数分解

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
typedef pair<int,int> pii;
const int maxn=sqrt(1e9)+1;
int tot=0;
pii pri[100];
ULL ans=0;
int cnt=0;
int tots=0;
int prime[maxn];
bool p[maxn];
void init()
{
	memset(p,1,sizeof(p));
	for(int i=2;i<maxn;i++)
	{
		if(p[i]) prime[tots++]=i;
		for(int j=0;j<tots&&prime[j]*i<maxn;j++)
		{
			p[prime[j]*i]=false;
			if(i%prime[j])break;
		}
	}
}
void dfs(int x,ULL y)
{
	if(x==tot)
	{
		ans=ans+y*y;
		return ;
	}
	dfs(x+1,y);
	for(int j=1;j<=pri[x].second;j++)
	{
		y=y*pri[x].first;
		dfs(x+1,y);
	}
}
void solve(int n)
{
	int tol=0;
	ans=0;cnt=1;
	tot=0;
	int temp=n;
	for(int i=0;prime[i]*prime[i]<=n&&i<tots;i++)
	{
		if(n%prime[i]==0)
		{
			tol=0;
			do n/=prime[i],tol++;
			while(n%prime[i]==0);
			cnt=cnt*(tol+1);
			pri[tot++]=pii(prime[i],tol);
		}
	}
	if(n!=1) pri[tot++]=pii(n,1),cnt=cnt*2;
	dfs(0,1);
	ans=ans-1ULL*cnt*temp;
}
int main()
{
	int t;
	scanf("%d",&t);
	int n;
	init();
	while(t--)
	{
		scanf("%d",&n);
		solve(n);
		printf("%llu\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值