Sum of gcd of Tuples (Hard)
题意:N个元素的元组,元素大小范围为1~K,求所有不同元组的最大公因数之和。
思路:
GCD学得跟翔一样的我吓哭了 反正就是不会,数也大,显然不能暴力做啊,没经验的本菜鸡就等着比完赛看题解了。
然后看到讨论区的一位大佬,名叫ritik_patel05,给出了吓死我的超神思路。
那我这里就直接开始翻译 就是逆向思维。题目的要求的是总和,那么只需求gcd==1至K的元组的数目,乘上对应的gcd,再进行求和。而我们知道,每个N元组的每个位置都是在1~K中选出一个数字。那么,gcd(1)的个数( n( gcd(1) ) )就是N元组的每个位置都是1的倍数的个数,减掉gcd(2)的个数、gcd(3)的个数,一直到gcd(k)的个数。
即,n( gcd( 1 ) ) = 元组中每个位置都是1的倍数的元组个数 - n( gcd( 2 ) ) - n( gcd( 3 ) ) - n( gcd( 4 ) ) - … - n( gcd( k ) )
而,元组中每个位置都是1的倍数的元组的个数为:1~K中1的倍数的个数^N。而1至K中1的倍数的个数就是K/1。
那么,n( gcd( 1 ) ) = k^n - sum( n( gcd( 1×m ) ) ) (1×m<=k)
推广:n( gcd( p ) ) = (k/p)^n - sum( n( gcd( p×m ) ) ) (p×m<=k)
至此,思路结束。
我真是太菜了啊。
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int maxn=100003;
typedef long long ll;
ll g[maxn];
ll fastpow(ll a,ll n){
a%=mod;
ll ans=1;
while(n){
if(n&1) ans=(ans*a)%mod;
a=(a*a)%mod;
n>>=1;
}
return ans;
}
int main(){
int n,k;
scanf("%d%d",&n,&k);
//gcd=m的个数*m
//个数:
//n(gcd(1)):k^n-sum(n(gcd(1*m)))(1*m<=k)
//n(gcd(2)):(k/2)^n-sum(n(gcd(2*m)))(2*m<=k)
//n(gcd(p)):(k/p)^n-sum(n(gcd(p*m)))(p*m<=k)
//n(gcd(k)):1
ll ans=0;
ll tmp,sum;
for(int i=k;i>=1;i--){
tmp=k/i;
g[i]=fastpow(tmp,n);
sum=0;
for(int j=2*i;j<=k;j+=i){
sum=(sum+g[j])%mod;
}
g[i]=(g[i]-sum+mod)%mod;//这里要再加上mod,防止为负数
ans=(ans+(g[i]*i)%mod)%mod;
}
printf("%lld\n",ans);
}