版权声明:转载注明出处,谢谢,有问题可以向博主联系 https://blog.csdn.net/VictoryCzt/article/details/85054670
给你两个正整数
n,m,求下面式子在
mod20101009意义下的值。
i=1∑nj=1∑mlcm(i,j)
n,m≤107
我们根据
lcm(ij)=gcd(i,j)ij,将原式化简成:
i=1∑nj=1∑mgcd(i,j)ij
根据套路,我们枚举
gcd,原式则化成:
(这里默认
n≤m,如果不满足则交换)
=d=1∑ni=1∑nj=1∑mdij[gcd(i,j)=d]=d=1∑ni=1∑⌊dn⌋j=1∑⌊dm⌋d(id)×(jd)[gcd(i,j)=1]
此时原式就可以变成:
d=1∑ndi=1∑⌊dn⌋j=1∑⌊dm⌋ij[gcd(i,j)=1]
然后又用莫比乌斯反演,我们可以将原式变成:
=d=1∑ndi=1∑⌊dn⌋j=1∑⌊dm⌋ijw∣i,w∣j∑μ(w)=d=1∑ndw=1∑⌊dn⌋μ(w)i=1∑⌊dwn⌋iwj=1∑⌊dwn⌋jw
然后我们令
S(n)=∑i=1ni=2n×(n+1),原式就可以变成:
=d=1∑ndw=1∑⌊dn⌋μ(w)w2S(⌊dwn⌋)S(⌊dwm⌋)
用线性筛筛出
μ(w)w2的前缀和,然后
S可以
O(1)算,所以我们可以对于
H(n,m)=∑w=1nμ(w)w2S(⌊wn⌋)S(⌊wm⌋)用数论分块计算,而对于
∑d=1nH(⌊dn⌋,⌊dm⌋)又数论分块算即可,复杂度最坏为
O(n+m)。
还是上面的问题,
n,m≤107,但是有
T≤104组询问。
所以我们必须要做到
O(n)预处理,每次
O(n
)的回答。
我们还是看上一个化简后的式子:
=d=1∑ndw=1∑⌊dn⌋μ(w)w2S(⌊dwn⌋)S(⌊dwm⌋)
还有一个套路就是,我们枚举
dw,所以令
T=dw,那么原式等于:
=T=1∑nS(⌊Tn⌋)S(⌊Tm⌋)d∣T∑dμ(dT)(dT)2=T=1∑nS(⌊Tn⌋)S(⌊Tm⌋)w∣T∑μ(w)w2wT=T=1∑nS(⌊Tn⌋)S(⌊Tm⌋)Tw∣T∑μ(w)w
对于前半部分我们每次数论分块求即可,而对于后面我们线性筛出它,然后求前缀和即可。
我们令
f(x)=x∑d∣xμ(x)x,那么它肯定是个积性函数(因为相当于可以写成
id⋅((μ⋅id)⨂1))根据欧拉筛的方法我们只需维护三个东西
cnt,fir,pow即可,分别表示:
cnt[i],
i的最小质因子指数;
fir[i],
i的最小质因子;
pow[i]表示
fir[i]cnt[i]。
然后我们来看
f(x)的计算方法:
- 当
x=p为质数,
f(x)=x∑d∣xμ(x)x=x×(μ(1)1+μ(x)x)=x(1−x)
- 当
x=pc时,
p为质数,那么
f(x)=x∑k=0cμ(pk)pk,由于
k>1时
μ(pk)=0,所以只考虑
k=0,1,那么
f(x)=xc(1−x)。
- 当
x=py,gcd(p,y)=1,根据积性函数定义
f(x)=f(p)f(y)
- 当
x=py,gcd(p,y)̸=1时,我们将
y中的
p提出,得到
x=pc+1pcy,那么此时两个就互质了,所以
f(x)=f(pcy)f(pc+1),其中
pc∣y。
代码就十分好写了,同样也可以过第一题:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=1e7+10,P=1e6+10;
const ll Mod=20101009;
ll prime[P],cnt;bool vis[M];
ll F[M],c[M],p[M],f[M],inv_2;
ll fpow(ll a,ll b){
ll ans=1;
for(;b;b>>=1,a=(a*a)%Mod){
if(b&1)ans=(ans*a)%Mod;
}
return ans;
}
void init(ll n){
F[1]=1;
for(int i=1;i<=n;i++)p[i]=f[i]=1;
for(ll i=2;i<=n;i++){
if(!vis[i]){
prime[++cnt]=i;
F[i]=(i-(i*i%Mod)+Mod)%Mod;
c[i]=1;f[i]=i;p[i]=i;
}
for(ll j=1,v;j<=cnt&&i*prime[j]<=n;j++){
v=i*prime[j];
vis[v]=1;
if(!(i%prime[j])){
c[v]=c[i]+1;f[v]=f[i];p[v]=p[i]*f[i];
F[v]=F[v/p[v]]*((p[v]-p[v]*f[v]%Mod+Mod)%Mod)%Mod;
break;
}
F[v]=(F[i]*F[prime[j]])%Mod;
c[v]=1;f[v]=prime[j];p[v]=prime[j];
}
}
for(int i=2;i<=n;i++)F[i]=(F[i]+F[i-1])%Mod;
}
ll S(ll a){if(a>=Mod)a%=Mod;return ((a*(a+1)%Mod)*inv_2)%Mod;}
ll calc(ll L,ll R){return ((F[R]-F[L-1])%Mod+Mod)%Mod;}
ll solve(ll n,ll m){
if(n>m)swap(n,m);
init(n);
inv_2=fpow(2,Mod-2);
ll ans=0;
for(ll i=1,j;i<=n;i=j+1){
j=min(n/(n/i),m/(m/i));
ans=(ans+S(n/i)*S(m/i)%Mod*calc(i,j)%Mod)%Mod;
}
return ans;
}
ll n,m;
int main(){
scanf("%lld%lld",&n,&m);
printf("%lld\n",solve(n,m));
return 0;
}
51Nod 1238 最小公倍数之和V3
还是一样的问题,只不过
n=m≤1010,只询问一次。
此时就不能线性筛了,必须要做到线性以下的求法,那么就是杜教筛了。
而前面化简的式子,若
f(x)=x∑d∣xμ(d)d能杜教筛出,那么就可以用之前的方法做,但是这并不好实现反正博主不会的啦QWQ。
所以我们考虑,在这个时候重新变换:
=d=1∑ndi=1∑⌊dn⌋j=1∑⌊dn⌋ij[gcd(i,j)=1]
我们可以先来看个式子:
i=1∑nj=1∑i[gcd(i,j)=1]ij
我们知道
φ(i)表示
1∼i−1中与
i互质的个数,而容易得知,
1∼i−1中与
i−1互质的数的和为
2iφ(i)
简单证明:因为对于一个数
d≤i−1,如果它与
i互质,根据欧几里得算法,
i−d也与
i互质,那么这一对互质的和就为
i,总共有
φ(i)个互质,总和就是
iφ(i),但是
d,i−d和
i−d,d算了两次,所以再除以2就是答案了,其中1要特殊考虑。
所以上面的那个式子可以写成:
=i=1∑nij=1∑ij[gcd(i,j)=1]=i=1∑ni2iφ(i)+[i=1]
那么我们看原式,就可以写成:
=d=1∑nd⎝⎛2×⎝⎛i=1∑⌊dn⌋i2iφ(i)+[i=1]⎠⎞−1⎠⎞=d=1∑nd⎝⎛i=1∑⌊dn⌋i2φ(i)⎠⎞
因为原式后半部分可以写成:
i=1∑⌊dn⌋i⎝⎛j=1∑ij[gcd(i,j)=1]+j=i+1∑⌊dn⌋j[gcd(i,j)=1]⎠⎞
而后面两部分就想一个正方形,边长为
⌊dn⌋,沿着对角线剪开一样,是对称的,所以取其中一个乘以
2即可。
详细来说,原式可以变成:
d=1∑nd⎝⎛2×⎝⎛i=1∑⌊dn⌋j=1∑iij⎠⎞−i=1∑⌊dn⌋i2⎠⎞
因为
(i,j),(j,i)会被算两次,所以乘以2,而
(i,i)只会被算一次所以减去,然后就可以用前面式子替换就可以得到,后面
∑d=1n∑i=1⌊dn⌋i2=2n×(n+1)减去
∑d=1nd∑i=1⌊dn⌋[i=1]=2n×(n+1)的刚好消掉。
那么原式就转换成了求:
d=1∑nd⎝⎛i=1∑⌊dn⌋i2φ(i)⎠⎞
我们令
f(x)=x2φ(x),然后令
g(x)=x2,容易得知(
⨂为狄利克雷卷积):
f⨂g(n)=d∣n∑f(d)g(dn)=d∣n∑d2φ(d)(dn)2=n2d∣n∑φ(d)
又因为
∑d∣nφ(d)=n,所以令:
h(n)=f⨂g(n)=n3
令
S(n)=∑i=1nf(i),因为
g(1)=1
带入杜教筛公式中可以得到:
S(n)=i=1∑nh(i)−i=2∑ng(i)S(⌊in⌋)
也就是:
S(n)=(2n×(n+1))2−i=2∑ni2S(⌊in⌋)
而
∑i=1ni2=6n×(n+1)×(2n+1),所以这个式子就很容易算了。
用杜教筛的方式先预处理
n32的答案,然后数论分块递归即可。
对于原式外面我们仍然数论分块求即可。
复杂度
O(n32)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e6+10,M=1e4+10;
const ll Mod=1e9+7;
ll prime[N],phi[N],cnt,n;
ll F[N],D[M],inv_2,inv_6;
ll fpow(ll a,ll b){
ll ans=1;
for(;b;b>>=1,a=(a*a)%Mod){
if(b&1)ans=(ans*a)%Mod;
}
return ans;
}
ll &Rec(ll a){
if(a<N) return F[a];
else return D[n/a];
}
bool vis[N];
ll Sqr(ll a){if(a>=Mod)a%=Mod;return a*a%Mod;}
void init(int up){
memset(F,-1,sizeof(F));
memset(D,-1,sizeof(D));
phi[1]=1;
inv_2=fpow(2,Mod-2);
inv_6=fpow(6,Mod-2);
for(int i=2;i<up;i++){
if(!vis[i]){
prime[++cnt]=i;
phi[i]=i-1;
}
for(int j=1,v;j<=cnt&&i*prime[j]<up;j++){
v=i*prime[j];
vis[v]=1;
if(!(i%prime[j])){
phi[v]=phi[i]*prime[j];
break;
}
phi[v]=phi[i]*phi[prime[j]];
}
}
F[0]=0;
for(int i=1;i<up;i++)F[i]=(F[i-1]+phi[i]*Sqr(i)%Mod)%Mod;
}
ll S(ll a){if(a>=Mod)a%=Mod;return (a*(a+1)%Mod)*inv_2%Mod;}
ll S2(ll a){if(a>=Mod)a%=Mod;return (a*(a+1)%Mod*((a+a+1)%Mod)%Mod)*inv_6%Mod;}
ll S3(ll a){if(a>=Mod)a%=Mod;return Sqr(S(a));}
ll Sarea(ll L,ll R){return ((S(R)-S(L-1))%Mod+Mod)%Mod;}
ll S2area(ll L,ll R){return ((S2(R)-S2(L-1))%Mod+Mod)%Mod;}
ll calc(ll x){
ll &ans=Rec(x);
if(ans!=-1) return ans;
ans=S3(x);
for(ll i=2,j;i<=x;i=j+1){
j=x/(x/i);
(ans-=(S2area(i,j)*calc(x/i))%Mod)%=Mod;
}
return (ans+Mod)%Mod;
}
ll solve(){
ll ans=0;
for(ll i=1,j;i<=n;i=j+1){
j=n/(n/i);
(ans+=(Sarea(i,j)*calc(n/i))%Mod)%=Mod;
}
return ans;
}
int main(){
scanf("%lld",&n);
init(min(n+1,1ll*N));
printf("%lld\n",solve());
return 0;
}
大佬的更好的讲解-by fwat Orz%%%【链接】
另一种方法-by Imagine Orz%%%【链接】
如果有错,或者有更好的方法,欢迎提出!(*`∀´*)ノ亻!
End
快要退役了,再努力搏一搏吧