【题目地址】
简易题意
给定 n , m n,m n,m,求下面式子在 m o d 1 0 9 + 7 {\rm mod}\ 10^9+7 mod 109+7意义下的值:
∑ i = 1 n ∑ j = 1 m φ ( i j ) \sum_{i=1}^n\sum_{j=1}^m\varphi(ij) i=1∑nj=1∑mφ(ij)
和这道题很像,但是这里 n ⩽ 1 0 5 , m ⩽ 1 0 9 n\leqslant 10^5,m\leqslant 10^9 n⩽105,m⩽109,数据范围完全不同了,而且只有单次询问,所以我们考虑 n n n很小,所以从后半部分下手。
我们令 S ( n , m ) = ∑ i = 1 m φ ( i n ) S(n,m)=\sum\limits_{i=1}^m\varphi(in) S(n,m)=i=1∑mφ(in),那么最后的式子就是 ∑ i = 1 n S ( i , m ) \sum\limits_{i=1}^nS(i,m) i=1∑nS(i,m),所以考虑如何快速求取 S S S函数。
现在只看这个式子:
∑ i = 1 m φ ( i n ) \sum_{i=1}^m\varphi(in) i=1∑mφ(in)
考虑如何把 φ ( i n ) \varphi(in) φ(in)拆开,根据求取 φ \varphi φ的式子:
n = ∏ p i ∣ n p i c i φ ( n ) = n ∏ p i ∣ n p i − 1 p i n=\prod_{p_i|n}p_i^{c_i} \\ \varphi(n)=n\prod_{p_i|n}\frac{p_i-1}{p_i} n=pi∣n∏piciφ(n)=npi∣n∏pipi−1
我们提取每种质因子的一个出来,那么就变成了:
a = ∏ p i ∣ n p i b = ∏ p i ∣ n p i c i − 1 n = a × b φ ( n ) = φ ( a ) × b a=\prod_{p_i|n}p_i \\ b=\prod_{p_i|n}p_i^{c_i-1} \\ n=a\times b \\ \varphi(n)=\varphi(a)\times b a=pi∣n∏pib=pi∣n∏pici−1n=a×bφ(n)=φ(a)×b
因为在例如 g c d ( i , p ) ̸ = 1 gcd(i,p)\not = 1 gcd(i,p)̸=1时, φ ( i × p ) = φ ( i ) × p \varphi(i\times p)=\varphi(i)\times p φ(i×p)=φ(i)×p,而每个质因子之间可以分开独立考虑,所以上面式子成立。
而 φ ( i × p ) = φ ( i ) × p \varphi(i\times p)=\varphi(i)\times p φ(i×p)=φ(i)×p的证明看这里:
摘取自友链博客【IN-there-by hdxrie】
所以我们把 n n n变成 a , b a,b a,b后,原式变成如下:
S ( n , m ) = b ∑ i = 1 m φ ( a i ) = b ∑ i = 1 m φ ( a g c d ( i , a ) ) φ ( i ) g c d ( i , a ) \begin{aligned} S(n,m)=&b\sum_{i=1}^m\varphi(ai) \\ =& b\sum_{i=1}^m\varphi(\frac{a}{gcd(i,a)})\varphi(i)gcd(i,a) \end{aligned} S(n,m)==bi=1∑mφ(ai)bi=1∑mφ(gcd(i,a)a)φ(i)gcd(i,a)
由于 a a a中全部是一次质因子的乘积,所以:
φ ( a i ) = a i ∏ p k ∣ a i p k − 1 p k = a i ∏ p k ∣ i p k − 1 p k ∏ p k ∣ a , p k ∤ i p k − 1 p k = i ∏ p k ∣ i p k − 1 p k a g c d ( i , a ) ∏ p k ∣ a g c d ( i , a ) p k − 1 p k × g c d ( i , a ) = φ ( i ) φ ( a g c d ( i , a ) ) g c d ( i , a ) \begin{aligned} \varphi(ai)=&ai\prod_{p_k|ai}\frac{p_k-1}{p_k} \\ =&ai\prod_{p_k|i}\frac{p_k-1}{p_k}\prod_{p_k|a,p_k\nmid i}\frac{p_k-1}{p_k} \\ =&i\prod_{p_k|i}\frac{p_k-1}{p_k}\frac{a}{gcd(i,a)}\prod_{p_k|\frac{a}{gcd(i,a)}}\frac{p_k-1}{p_k}\times gcd(i,a) \\ =&\varphi(i)\varphi(\frac{a}{gcd(i,a)})gcd(i,a) \end{aligned} φ(ai)====aipk∣ai∏pkpk−1aipk∣i∏pkpk−1pk∣a,pk∤i∏pkpk−1ipk∣i∏pkpk−1gcd(i,a)apk∣gcd(i,a)a∏pkpk−1×gcd(i,a)φ(i)φ(gcd(i,a)a)gcd(i,a)
这里,我们考虑将其拆开,一部分是属于 i i i的质因子,而只属于 a a a的也就是除去公共质因子的部分,相当于属于 a g c d ( i , a ) \frac{a}{gcd(i,a)} gcd(i,a)a的,但是最后还少了个 g c d ( i , a ) gcd(i,a) gcd(i,a),再乘上即可。而且这里由于 a a a的所有质因子都只出现一次,所以除以了 g c d ( i , a ) gcd(i,a) gcd(i,a)后肯定与 i i i互质,不会有重复的 p k − 1 p k \frac{p_k-1}{p_k} pkpk−1。
然后我们根据 φ ∗ 1 = i d \varphi*\mathbf 1=id φ∗1=id,将后面的 g c d ( i , a ) = ∑ d ∣ g c d ( i , a ) φ ( d ) gcd(i,a)=\sum_{d|gcd(i,a)}\varphi(d) gcd(i,a)=∑d∣gcd(i,a)φ(d),所以:
S ( n , m ) = b ∑ i = 1 m φ ( a g c d ( i , a ) ) φ ( i ) ∑ d ∣ g c d ( i , a ) φ ( d ) \begin{aligned} S(n,m)=& b\sum_{i=1}^m\varphi(\frac{a}{gcd(i,a)})\varphi(i)\sum_{d|gcd(i,a)}\varphi(d) \end{aligned} S(n,m)=bi=1∑mφ(gcd(i,a)a)φ(i)d∣gcd(i,a)∑φ(d)
由于 a a a中每个质因子只出现一次,所以我们可以将 a g c d ( i , a ) \frac{a}{gcd(i,a)} gcd(i,a)a乘到 ∑ d ∣ g c d ( i , a ) φ ( d ) \sum_{d|gcd(i,a)}\varphi(d) ∑d∣gcd(i,a)φ(d)中的每一项,此时,由于 a g c d ( i , a ) \frac{a}{gcd(i,a)} gcd(i,a)a肯定与每个 d d d互质,根据积性函数的定义:
a g c d ( i , a ) ∑ d ∣ g c d ( i , a ) φ ( d ) = ∑ d ∣ g c d ( i , a ) φ ( a d g c d ( i , a ) ) \frac{a}{gcd(i,a)}\sum_{d|gcd(i,a)}\varphi(d)=\sum_{d|gcd(i,a)}\varphi(\frac{ad}{gcd(i,a)}) gcd(i,a)ad∣gcd(i,a)∑φ(d)=d∣gcd(i,a)∑φ(gcd(i,a)ad)
其实就相当于等于:
∑ d ∣ g c d ( i , a ) φ ( a g c d ( i , a ) d ) = ∑ d ∣ g c d ( i , a ) φ ( a d ) \sum_{d|gcd(i,a)}\varphi(\frac{a}{\frac{gcd(i,a)}{d}})=\sum_{d|gcd(i,a)}\varphi(\frac{a}{d}) d∣gcd(i,a)∑φ(dgcd(i,a)a)=d∣gcd(i,a)∑φ(da)
因为枚举 g c d ( i , a ) d \frac{gcd(i,a)}{d} dgcd(i,a), d d d是一样的。
所以我们继续化简:
S ( n , m ) = b ∑ i = 1 m φ ( i ) ∑ d ∣ g c d ( i , a ) φ ( a d ) \begin{aligned} S(n,m)=& b\sum_{i=1}^m\varphi(i)\sum_{d|gcd(i,a)}\varphi(\frac{a}{d}) \end{aligned} S(n,m)=bi=1∑mφ(i)d∣gcd(i,a)∑φ(da)
我们交换枚举顺序即可得到:
S ( n , m ) = b ∑ d ∣ a φ ( a d ) ∑ d ∣ i m φ ( i ) = b ∑ d ∣ a φ ( a d ) ∑ i = 1 ⌊ m d ⌋ φ ( d i ) \begin{aligned} S(n,m)=& b\sum_{d|a}\varphi(\frac{a}{d})\sum_{d|i}^{m}\varphi(i) \\ =& b\sum_{d|a}\varphi(\frac{a}{d})\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}\varphi(di) \end{aligned} S(n,m)==bd∣a∑φ(da)d∣i∑mφ(i)bd∣a∑φ(da)i=1∑⌊dm⌋φ(di)
然后可以发现后面部分就是一个子问题了,所以递归求就好啦。
S ( n , m ) = b ∑ d ∣ a φ ( a d ) S ( d , ⌊ m d ⌋ ) \begin{aligned} S(n,m)=& b\sum_{d|a}\varphi(\frac{a}{d})S(d,\lfloor\frac{m}{d}\rfloor) \end{aligned} S(n,m)=bd∣a∑φ(da)S(d,⌊dm⌋)
复杂度和杜教筛类似。
代码~~
#include<vector>
#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define lowbit(a) ((a)&(-(a)))
using namespace std;
const int M=1e6+10,N=1e5+10,S=1<<20;
const ll Mod=1e9+7;
ll prime[N],phi[M],F[M],last[M],cnt;
bool vis[M];
int bit[S];
void init(){
phi[1]=1;
bit[1]=0;
for(int i=1;i<=19;i++)bit[1<<i]=i;
for(ll i=2;i<M;i++){
if(!vis[i]){
prime[++cnt]=i;
phi[i]=i-1;last[i]=1;\\last记录上次的质因子
}
for(ll j=1,v;j<=cnt&&i*prime[j]<M;j++){
v=i*prime[j];
vis[v]=1;last[v]=i;
if(!(i%prime[j])){
phi[v]=phi[i]*prime[j];
break;
}
phi[v]=phi[i]*phi[prime[j]];
}
}
for(int i=1;i<M;i++)F[i]=(F[i-1]+phi[i])%Mod;
}
map <ll,ll> mp_phi;
map <ll,ll> mp_cal[N];
ll calc_phi(ll n){
if(n<M) return F[n];//杜教筛求phi的前缀和
if(mp_phi.count(n)) return mp_phi[n];
ll ans=((n*(n+1ll))>>1)%Mod;
for(ll i=2,j;i<=n;i=j+1){
j=(n/(n/i));
ans=(ans-((j-i+1)%Mod)*calc_phi(n/i)%Mod)%Mod;
}
if(ans<0)ans+=Mod;
return mp_phi[n]=ans;
}
map <ll,ll> mp_p;
ll Getp(ll a){
if(a<M) return phi[a];
if(mp_p.count(a)) return mp_p[a];
ll t=a,tmp=a;
for(ll j=2;j*j<=a;j++){
if(t%j==0) {
a=a/j*(j-1);
while(t%j==0) t/=j;
}
}if(t>1) a=a/t*(t-1);
return mp_p[tmp]=a;
}//求单独一个phi
ll calc(ll n,ll m){
if(n==1) return calc_phi(m);//n=1时就是phi的前缀和
if(m==1) return Getp(n);//m=1就是一个phi
if(!m||!n) return 0;//为0就返回0
if(mp_cal[n].count(m)) return mp_cal[n][m];//记忆化
vector <ll> div;div.clear();
ll ans=0,x=1,y,w=1,tc=n;
while(n>1){
y=n/last[n];w*=y;n=last[n];
div.push_back(y);
while(!(n%y))n/=y,x*=y;
}
//w为a,x为b
n=tc;
int sze=div.size(),up=1<<sze;
for(int i=0;i<up;i++){
ll d=1ll;
for(int j=i;j>0;j-=lowbit(j))d*=div[bit[lowbit(j)]];
ans=(ans+(Getp(w/d)*calc(d,m/d))%Mod)%Mod;
}//2进制枚举约数的质因子组成
ans=(ans*x)%Mod;//最后乘以b
return mp_cal[n][m]=ans;
}
int n,m;ll ans;
int main(){
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)ans=(ans+calc(i,m))%Mod;//累加
printf("%lld\n",ans);
return 0;
}
另一种实现方式,较为简单,原理差不多:
挂个链接Orz%%%-IN