矩阵求和 题解(欧拉函数)

题目链接

题目描述

经过重重笔试面试的考验,小明成功进入 Macrohard 公司工作。
今天小明的任务是填满这么一张表:
表有 n 行 n 列,行和列的编号都从1算起。
其中第 i 行第 j 个元素的值是 gcd(i, j)的平方,
gcd 表示最大公约数,以下是这个表的前四行的前四列:
1 1 1 1
1 4 1 4
1 1 9 1
1 4 1 16

小明突然冒出一个奇怪的想法,他想知道这张表中所有元素的和。
由于表过于庞大,他希望借助计算机的力量。

「输入格式」
一行一个正整数 n 意义见题。

「输出格式」
一行一个数,表示所有元素的和。由于答案比较大,请输出模 (10^9 + 7)(即:十亿零七) 后的结果。

「样例输入」
4

「样例输出」
48

「数据范围」
n <= 10^7

题目思路

本质上就是找有多少个\((i,j)\)使得\(\gcd(i,j)=k\)

可以转化为\(n/k\)以内有几对\(gcd(i,j)=1\)

那就是\(phi[i]\)的前缀和,然后求总和

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=1e7+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,num;
int phi[maxn],prime[maxn];
ll pre[maxn];
bool flag[maxn];
void euler(int n){
	phi[1]=1;//1要特判
	pre[1]=1;
	for (int i=2;i<=n;i++){
		if (flag[i]==0){//这代表i是质数
			prime[++num]=i;
			phi[i]=i-1;
		}
		for (int j=1;j<=num&&prime[j]*i<=n;j++)//经典的欧拉筛写法
		{
			flag[i*prime[j]]=1;//先把这个合数标记掉
			if (i%prime[j]==0){
				phi[i*prime[j]]=phi[i]*prime[j];//若prime[j]是i的质因子,则根据计算公式,i已经包括i*prime[j]的所有质因子
				break;//经典欧拉筛的核心语句,这样能保证每个数只会被自己最小的因子筛掉一次
			}else{
                phi[i*prime[j]]=phi[i]*phi[prime[j]];//利用了欧拉函数是个积性函数的性质
			}
		}
		pre[i]=pre[i-1]+2*phi[i];
	}
}
signed main(){
    scanf("%d",&n);
    euler(n);
    ll ans=0;
    for(int i=1;i<=n;i++){
        ans=(ans+pre[n/i]*i%mod*i)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

posted @ 2022-03-08 23:37  hunxuewangzi  阅读(130)  评论(0编辑  收藏  举报