https://www.51nod.com/Challenge/Problem.html#!#problemId=1363
求(sumlimits_{i=1}^{n}lcm(i,n))
先换成gcd:
(sumlimits_{i=1}^{n}frac{i*n}{gcd(i,n)})
显而易见,枚举g:
$ n * sumlimits_{g|n} frac{1}{g} sumlimits_{i=1}^{n} i*[gcd(i,n)==g] $
提g,没有下整符号:
$ n * sumlimits_{g|n} frac{1}{g} * g sumlimits_{i=1}^{frac{n}{g}} i*[gcd(i,frac{n}{g})==1] $
考虑子问题:
$sumlimits_{i=1}^{n} i*[gcd(i,n)==1] $
也就是n以内和n互质的数的和。
显然可以枚举因数d把他们减掉:
$sumlimits_{i=1}^{n} i + sumlimits_{d|n,d!=1}mu(d)(d+2d+3d+...+n) $
显然前面那堆等于 (d==1) 的结果。
$sumlimits_{d|n}mu(d)(d+2d+3d+...+n) $
比如求36的互质数的和时,6在枚举2和枚举3的时候算重,要加回去,4在枚举2的时候算过,不用算。
化简:
$ sumlimits_{d|n}mu(d)d(1+2+3+...+frac{n}{d}) (
)sumlimits_{d|n}mu(d)dfrac{(1+frac{n}{d})*frac{n}{d}}{2} $
即:
$frac{n}{2}sumlimits_{d|n}mu(d)(1+frac{n}{d}) (
里面分配率:
)frac{n}{2}(sumlimits_{d|n}mu(d)+sumlimits_{d|n}mu(d)frac{n}{d}) $
然后一换:
(frac{n}{2}([n==1]+varphi(n) ))
求这个东西的复杂度很显然,预处理因子的欧拉函数是根号的,求单个n的欧拉函数也是根号的,所以整个式子就是根号的。
记这个子问题为 (p(n)=sumlimits_{i=1}^{n} i*[gcd(i,n)==1] = frac{n}{2}([n==1]+varphi(n) )),求解它的复杂度就来源于欧拉函数
回到$ n * sumlimits_{g|n} p(frac{n}{g}) $
非常明显根号以内的欧拉函数可以重复利用,然后单独求一个最大的。
总体复杂度是根号的。理论上根号是过不了的,1.7e8左右,但是看别人搞什么质因数分解?这个不也是最坏根号的吗?我觉得他们能行我也能行。
果断T了,质因数分解假如遇到合数应该是跑得比我这快得多的。
$ n * sumlimits_{g|n} p(frac{n}{g}) = frac{n}{2} (sumlimits_{g|n} gvarphi(g))+frac{n}{2}$
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read() {
ll x=0;
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
do {
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
} while(c>='0'&&c<='9');
return x;
}
inline void write(ll x) {
//printf("%lld
",x);
if(x>9) {
write(x/10);
}
putchar(x%10+'0');
return;
}
const int MAXN=5e6;
//有用的质数恰好有3402个
const int mod=1e9+7;
const int inv2=mod+1>>1;
int pri[MAXN+1];
int &pritop=pri[0];
int phi[MAXN+1];
void sieve(int n=MAXN) {
phi[1]=1;
for(int i=2; i<=n; i++) {
if(!phi[i]) {
pri[++pritop]=i;
phi[i]=i-1;
}
for(int j=1; j<=pritop&&i*pri[j]<=n; j++) {
int t=i*pri[j];
if(i%pri[j]) {
phi[t]=phi[i]*phi[pri[j]];
} else {
phi[t]=phi[i]*pri[j];
break;
}
}
}
}
inline int get_phi_pk(int n,int p){
int res=n-n/p;
return res;
}
int get_phi(int n){
int res=n;
for(int i=1;i<=3402;i++){
if(n%pri[i]==0){
res=res/pri[i]*(pri[i]-1);
while(n%pri[i]==0)
n/=pri[i];
}
if(n==1)
return res;
}
res=res/n*(n-1);
return res;
}
int p(int n){
ll res=(n==1);
if(n<=MAXN)
res+=phi[n];
else
res+=get_phi(n);
res=(1ll*n*res)>>1;
if(res>=mod)
res%=mod;
//printf("p[%d]=%lld
",n,res);
return res;
}
int fac[80][2];
int ftop=1;
int q(int n){
int cn=n;
ll res=1;
for(int i=1;pri[i]*pri[i]<=n;i++){
if(n%pri[i]==0){
ll tmpsum=1;
ll pk=1;
fac[ftop][0]=pri[i];
fac[ftop][1]=0;
while(n%pri[i]==0){
pk*=pri[i];
if(pk>=MAXN)
tmpsum+=pk*get_phi_pk(pk,pri[i]);
else
tmpsum+=pk*phi[pk];
fac[ftop][1]++;
n/=pri[i];
}
tmpsum%=mod;
res*=tmpsum;
res%=mod;
}
}
if(n!=1){
ll tmpsum=1+1ll*n*(n-1);
res*=tmpsum;
res%=mod;
}
//printf("q[%d]=%lld
",cn,res);
return res;
}
ll ans(int n){
ll res=(q(n)+1);
res*=n;
res%=mod;
res*=inv2;
res%=mod;
return res;
}
inline void solve() {
sieve();
int t=read();
while(t--){
int n=read();
write(ans(n));
putchar('
');
}
}
int main() {
#ifdef Yinku
freopen("Yinku.in","r",stdin);
#endif // Yinku
solve();
return 0;
}
上面这个有很多多余操作,比如我现在根本就不关心欧拉函数怎么求了,也没必要给他们初始化这么多了,把要用的质数筛出来就结束了。欧拉函数在pk位置的求法就是(varphi(p^k)=p^k-(p^k)/p=(p-1)varphi(p^{k-1}))
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read() {
int x=0;
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
do {
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
} while(c>='0'&&c<='9');
return x;
}
inline void write(int x) {
//printf("%lld
",x);
if(x>9) {
write(x/10);
}
putchar(x%10+'0');
return;
}
const int MAXN=4e5;
const int mod=1e9+7;
const int inv2=mod+1>>1;
int pri[MAXN+1];
int &pritop=pri[0];
int phi[MAXN+1];
void sieve(int n=MAXN) {
phi[1]=1;
for(int i=2; i<=n; i++) {
if(!phi[i]) {
pri[++pritop]=i;
phi[i]=i-1;
}
for(int j=1; j<=pritop&&i*pri[j]<=n; j++) {
int t=i*pri[j];
if(i%pri[j]) {
phi[t]=phi[i]*phi[pri[j]];
} else {
phi[t]=phi[i]*pri[j];
break;
}
}
}
}
int q(int n){
int cn=n;
ll res=1;
for(int i=1;pri[i]*pri[i]<=n;i++){
if(n%pri[i]==0){
ll tmpsum=1;
ll pk=1;
while(n%pri[i]==0){
pk*=pri[i];
tmpsum+=pk*(pk-pk/pri[i]);
n/=pri[i];
}
if(tmpsum>=mod)
tmpsum%=mod;
res*=tmpsum;
if(res>=mod)
res%=mod;
}
}
if(n!=1){
ll tmpsum=1ll*n*(n-1)+1;
res*=tmpsum;
if(res>=mod)
res%=mod;
res%=mod;
}
//printf("q[%d]=%lld
",cn,res);
return res;
}
int ans(int n){
ll res=q(n)+1;
res*=n;
if(res>=mod)
res%=mod;
res*=inv2;
if(res>=mod)
res%=mod;
return res;
}
inline void solve() {
sieve();
int t=read();
while(t--){
int n=read();
write(ans(n));
putchar('
');
}
}
int main() {
#ifdef Yinku
freopen("Yinku.in","r",stdin);
#endif // Yinku
solve();
return 0;
}