神奇题目,
话说是WC2018冬眠营的加群密码(1-100*群号的phi和%1000000007)
然后我就了解了一下,其实就是利用了一些积性函数的性质,
减少了一些不必要的运算。
ACcode:还要Hash+预处理5000000以下phi前缀和,恶心
另外,分块优化时也要用long long,2833855836死循环,1833855836就秒出,当时竟然没看出来是炸int了
最重要的思想:如果能通过狄利克雷卷积构造一个更好计算前缀和的函数,且用于卷积的另一个函数也易计算,则可以简化计算过程
核心式子:
令S(n) = sigma( F(i) ( 1<=i<=n ) )
对任意函数G有 G(i)*F(j) ( 1<= i * j <=n ) = G(i) * S( n/i ) (1<=i <=n)
所以我们只需要构造G函数,使得F函数被(各种方式)消掉,剩下的S函数我们就可以用分块优化了。
/*
sigma( phi(i)(1<=i<=d) )= F(d)
and sigma( phi(i)(i|d) )=d
phi(d)=d-sigma( phi(i)(i|d && i<d) );
F(d)=sigma( phi(i)(1<=i<=d) )
=sigma( i - sigma( phi(j)(j|i && j<i) ) (1<=i<=d) )
=d*(d+1)/2 - sigma( sigma( phi(j)(j|i && j<i) ) ( 1<=i<=d ))
=d*(d+1)/2 - sigma( sigma( phi(j)(1<=j<=(d/i))) ( 2<=i<=d ))
=d*(d+1)/2 - sigma( F[d/i](2<=i<=d) )
*/
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define LL long long
using namespace std;
const LL hashmod=2333333;
const LL mod=1000000007;
const LL inv2=500000004;
const LL maxn=5000000;
struct HashMap{
int info[hashmod],Prev[hashmod],siz;
LL key[hashmod],val[hashmod];
void push(LL a,LL b){
int c=a%hashmod;
key[++siz]=a;
val[siz]=b;
Prev[siz]=info[c];
info[c]=siz;
}
LL operator [](const LL k){
int c=k%hashmod;
for(int i=info[c];i;i=Prev[i])
if(key[i]==k)
return val[i];
return -1;
}
}F;
int pr[maxn/9],cnt;
bool vis[maxn];
LL phi[maxn];
void Get_phi(){
phi[1]=1;
for(int i=2;i<maxn;i++){
if(!vis[i]) pr[cnt++]=i,phi[i]=i-1;
for(int j=0;j<cnt && pr[j]*i<maxn;j++){
vis[i*pr[j]]=1;
if(i%pr[j]==0){
phi[i*pr[j]]=phi[i]*pr[j]%mod;
break;
}
phi[i*pr[j]]=phi[i]*(pr[j]-1)%mod;
}
phi[i]=(phi[i]+phi[i-1])%mod;
}
}
LL Solve(LL now){
if(now < maxn) return phi[now];
LL tmp=F[now],res;
if(tmp==-1){
res=now%mod;
tmp=0;
LL last;
for(LL i=2;i<=now;i=last+1){
last=now/(now/i);
tmp=(tmp+(last-i+1)*Solve(now/i)%mod)%mod;
}
tmp=(res*(res+1)%mod*inv2%mod-tmp+mod)%mod;
F.push(now,tmp);
}
return tmp;
}
int main(){
LL n;
Get_phi();
scanf("%lld",&n);
printf("%lld",Solve(n));
}
int info[hashmod],Prev[hashmod],siz;
LL key[hashmod],val[hashmod];
void push(LL a,LL b){
int c=a%hashmod;
key[++siz]=a;
val[siz]=b;
Prev[siz]=info[c];
info[c]=siz;
}
LL operator [](const LL k){
int c=k%hashmod;
for(int i=info[c];i;i=Prev[i])
if(key[i]==k)
return val[i];
return -1;
}
}F;
int pr[maxn/9],cnt;
bool vis[maxn];
LL phi[maxn];
void Get_phi(){
phi[1]=1;
for(int i=2;i<maxn;i++){
if(!vis[i]) pr[cnt++]=i,phi[i]=i-1;
for(int j=0;j<cnt && pr[j]*i<maxn;j++){
vis[i*pr[j]]=1;
if(i%pr[j]==0){
phi[i*pr[j]]=phi[i]*pr[j]%mod;
break;
}
phi[i*pr[j]]=phi[i]*(pr[j]-1)%mod;
}
phi[i]=(phi[i]+phi[i-1])%mod;
}
}
LL Solve(LL now){
if(now < maxn) return phi[now];
LL tmp=F[now],res;
if(tmp==-1){
res=now%mod;
tmp=0;
LL last;
for(LL i=2;i<=now;i=last+1){
last=now/(now/i);
tmp=(tmp+(last-i+1)*Solve(now/i)%mod)%mod;
}
tmp=(res*(res+1)%mod*inv2%mod-tmp+mod)%mod;
F.push(now,tmp);
}
return tmp;
}
int main(){
LL n;
Get_phi();
scanf("%lld",&n);
printf("%lld",Solve(n));
}