Description
给出一个n,求1-n这n个数,同n的最小公倍数的和。
例如:n = 6,1,2,3,4,5,6 同6的最小公倍数分别为6,6,6,12,30,6,加在一起 = 66。
由于结果很大,输出Mod 1000000007的结果。
Solution
这道题化简到一半,差点废了,后来经高人指点才明白……
ans=∑i=1ni∗ngcd(i,n)=n∗∑d|n∑i=1n/di[gcd(i,n/d)==1]
我们发现当存在i与x互质时, x-i与x也是对称的,换言之,与x对称的数是对称存在的,两两相加和为x,除了1。
ans=n+n∗∑d|nϕ(n/d)∗n/d2=n+n2∗∑d|n,d>1ϕ(d)∗d
我们把n的每个质因子提出来,算出它对答案的贡献,然后乘起来就可以。形如(a+b)*(c+d)=ac+ad+bc+bd
令n=∏i=1Bpiai
∑d|nϕ(d)∗d=∏i=1B∑j=0aiϕ(pij)∗pij=∏i=1B1+∑j=1ai(pi−1)∗pi2j−1=∏i=1B1+(pi−1)∗pi2ai+1−pipi2−1=∏i=1B1+pi2ai+1−pipi+1
最后只要把d=1的状态剪去即可,即减1。
令T=∏i=1B1+pi2ai+1−pipi+1
ans=n+n2(T−1)
。
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn=1e5;
const ll mo=1e9+7,mo2=5e8+4;
ll d[maxn],bz[maxn+5];
ll n,m,i,t,j,k,l,x,y,z,ans;
ll mi(ll x,ll y){
if (y==1) return x;
ll t=mi(x,y/2);
if (y%2==0) return t*t%mo;return t*t%mo*x%mo;
}
int main(){
// freopen("data.in","r",stdin);
scanf("%lld",&n);
for (i=2;i<=maxn;i++){
if (!bz[i]) d[++d[0]]=i;
for (j=1;j<=d[0];j++){
if (i*d[j]>maxn) break;
bz[i*d[j]]=1;
if (i%d[j]==0)break;
}
}
for (;n;n--){
scanf("%lld",&x);ans=1;y=x;
for (i=1;d[i]*d[i]<=x;i++){
if (x%d[i]) continue;
t=0;k=1;
while (x%d[i]==0) x/=d[i],t++;
k+=d[i]*(mi(d[i],2*t)-1)%mo*mi(d[i]+1,mo-2)%mo;
ans=ans*k%mo;
}
k=(1+(x-1)*x)%mo;
ans=ans*k%mo;
ans--;
ans=(ans*y%mo*mo2%mo+y)%mo;
printf("%lld\n",ans);
}
}