51nod 1237 最大公约数之和 V3

文章目录

题目链接:

http://www.51nod.com/Challenge/Problem.html#!#problemId=1237
我们想要求所有的 g c d ( i , j ) gcd(i,j) gcd(i,j)的和,换一个思路就是看 g c d ( i , j ) = d gcd(i,j)=d gcd(i,j)=d对答案的贡献,(感觉好多题都是换一种思路来看对答案的贡献的),那如果能够快速求出 g c d ( i , j ) = d gcd(i,j)=d gcd(i,j)=d的有几个就好了
假如说 g c d ( i , j ) = d gcd(i,j)=d gcd(i,j)=d的有 c n t ( d ) cnt(d) cnt(d)个,那么 a n s = ∑ d = 1 n d ⋅ c n t ( d ) ans=\sum_{d=1}^nd \cdot cnt(d) ans=d=1ndcnt(d)

那么现在结论就是:
c n t ( d ) = 2 ⋅ S ( n d ) − 1 cnt(d)=2\cdot S(\frac{n}{d})-1 cnt(d)=2S(dn)1
其中S(n)表示欧拉函数前缀和:
S ( n ) = ∑ i = 1 n φ ( i ) S(n)=\sum_{i=1}^n\varphi(i) S(n)=i=1nφ(i)

那么为什么喃?
先来看这个:
∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = = 1 ] = 2 ∑ i = 1 n ∑ j = 1 i − 1 [ g c d ( i , j ) = = 1 ] + 1 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2\sum_{i=1}^n\sum_{j=1}^{i-1}[gcd(i,j)==1]+1 i=1nj=1n[gcd(i,j)==1]=2i=1nj=1i1[gcd(i,j)==1]+1
这个还是很好理解的,因为 g c d ( i , j ) gcd(i,j) gcd(i,j) g c d ( j , i ) gcd(j,i) gcd(j,i)是对称的,主对角线分成两半就行
在这里插入图片描述
加1是因为 g c d ( 1 , 1 ) = 1 gcd(1,1)=1 gcd(1,1)=1
然后再修改一哈,把 g c d ( 1 , 1 ) gcd(1,1) gcd(1,1)加进去, j j j 1 1 1 i i i而不是 i − 1 i-1 i1
∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = = 1 ] = 2 ∑ i = 1 n ∑ j = 1 i [ g c d ( i , j ) = = 1 ] − 1 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2\sum_{i=1}^n\sum_{j=1}^{i}[gcd(i,j)==1]-1 i=1nj=1n[gcd(i,j)==1]=2i=1nj=1i[gcd(i,j)==1]1
有了这个之后,来看里面那层求和,其实这层求和就是欧拉函数的定义,所以
∑ j = 1 i [ g c d ( i , j ) = = 1 ] = φ ( i ) \sum_{j=1}^{i}[gcd(i,j)==1]=\varphi(i) j=1i[gcd(i,j)==1]=φ(i)

所以: ∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = = 1 ] = 2 ∑ i = 1 n φ ( i ) − 1 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2\sum_{i=1}^n\varphi(i)-1 i=1nj=1n[gcd(i,j)==1]=2i=1nφ(i)1
所以:
∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = = 1 ] = 2 S ( n ) − 1 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2S(n)-1 i=1nj=1n[gcd(i,j)==1]=2S(n)1

那现在要找 g c d ( i , j ) = d gcd(i,j)=d gcd(i,j)=d的个数就是求 ∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = = d ] \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==d] i=1nj=1n[gcd(i,j)==d]

因为如果: g c d ( i , j ) = = d gcd(i,j)==d gcd(i,j)==d,那么 g c d ( i d , j d ) = 1 gcd(\frac{i}{d},\frac{j}{d})=1 gcd(di,dj)=1
所以就有:
∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = = d ] = ∑ i = 1 n d ∑ j = 1 n d [ g c d ( i , j ) = = 1 ] = 2 ⋅ S ( n d ) − 1 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==d]=\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{n}{d}}[gcd(i,j)==1]=2\cdot S(\frac{n}{d})-1 i=1nj=1n[gcd(i,j)==d]=i=1dnj=1dn[gcd(i,j)==1]=2S(dn)1

好有了公式 a n s = ∑ d = 1 n d ⋅ ( 2 ⋅ S ( n d ) − 1 ) ans=\sum_{d=1}^nd \cdot (2\cdot S(\frac{n}{d})-1) ans=d=1nd(2S(dn)1)之后,貌似 d d d还是要从1遍历到n,还是至少都是线性复杂度啊

这里就用到了整除分块了, n d \frac{n}{d} dn不变的这段区间,虽然 d d d会变,但是是公差为1的等差数列,所以这一段的d也能一口气计算,所以整个复杂度就低了

#include"bits/stdc++.h"
using namespace std;
const int maxn=1e6+5;
typedef long long LL;
const LL MOD=1e9+7;
const LL inv2=5e8+4;
bool vis[maxn];
LL phi[maxn],Sphi[maxn];//欧拉函数,欧拉函数前缀和 
vector<int>prime;
map<LL,LL>Mp;
void PHI(int n)
{
	memset(vis,1,sizeof(vis));
	phi[1]=1;
	Sphi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(vis[i])
		{
			prime.push_back(i);
			phi[i]=i-1;
		}
		for(int j=0;j<prime.size()&&i*prime[j]<=n;j++)
		{
			vis[i*prime[j]]=0;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
		Sphi[i]=(Sphi[i-1]+phi[i])%MOD;
	}
}
LL S(LL n)//计算欧拉函数前缀和 
{
	if(n<=maxn-5)return Sphi[n];
	if(Mp[n])return Mp[n];
	LL res=(n%MOD)*((n+1)%MOD)%MOD*inv2%MOD;
	for(LL i=2,j;i<=n;i=j+1)
	{
		j=n/(n/i);
		res-=(LL)(j-i+1)%MOD*S(n/i)%MOD;
		res%=MOD;
	}
	res=(res+MOD)%MOD;
	return Mp[n]=res;
}
LL calc(LL i,LL j)//计算从i到j的公差为1的等差数列的和 
{
	LL res=(i+j)%MOD*(j-i+1LL)%MOD*inv2%MOD;
	return res%MOD;
 } 
int main()
{
	PHI(maxn-5);
	LL N;
	while(cin>>N)
	{
		LL res=0;
		for(LL d=1,j;d<=N;d=j+1)
		{
			j=N/(N/d);
			res+=calc(d,j)*(2LL*S(N/d)-1)%MOD;
			res%=MOD;
		}
		res=(res+MOD)%MOD;
		cout<<res<<endl;
	}
}

### 关于51Nod平台上编号为1020的问题详情与解答 #### 问题描述 在51Nod平台上的第1020号问题是关于计算两个大整数相加的结果[^1]。给定两个正整数A和B,长度不超过10^6位,要求编写程序来求解这两个数的和。 #### 输入格式说明 输入数据由多组测试案例组成;每组测试案例占两行,分别表示要相加的大整数A和B。对于每一组测试案例,应当单独输出一行结果,即A+B的值。 #### 解决方案概述 解决此问题的关键在于处理超大数据类型的运算,在大多数编程语言中内置的数据类型无法直接支持如此大规模数值的操作。因此,可以采用字符串的方式来存储这些大整数,并实现逐位相加逻辑,同时考虑进位情况。 下面是一个Python版本的具体实现方法: ```python def add_large_numbers(a: str, b: str) -> str: # Reverse strings to make addition easier from least significant digit a = a[::-1] b = b[::-1] carry = 0 result = [] max_length = max(len(a), len(b)) for i in range(max_length): digit_a = int(a[i]) if i < len(a) else 0 digit_b = int(b[i]) if i < len(b) else 0 total = digit_a + digit_b + carry carry = total // 10 current_digit = total % 10 result.append(str(current_digit)) if carry != 0: result.append(str(carry)) return &#39;&#39;.join(reversed(result)) if __name__ == "__main__": while True: try: num1 = input().strip() num2 = input().strip() print(add_large_numbers(num1, num2)) except EOFError: break ``` 该代码片段定义了一个函数`add_large_numbers`用于接收两个作为参数传入的大整数(形式上为字符串),并返回它们之和同样作为一个字符串。通过反转输入字符串使得最低有效位位于索引位置0处从而简化了按位累加的过程。最后再将得到的结果列表反向拼接成最终答案输出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值