51Nod2026 Gcd and Lcm-題解

VictoryCzt發表於2018-12-21

題目地址

題意簡述

我們令f(n)=dnμ(d)df(n)=\sum_{d|n}\mu(d)d,求下面式子在模998244353998244353後的值:

i=1nj=1nf(gcd(i,j))×f(lcm(i,j)) \sum_{i=1}^n\sum_{j=1}^nf(gcd(i,j))\times f(lcm(i,j))

其中gcdgcd為最大公約數,lcmlcm為最小公倍數。


我們先來看一個例子:

求:
i=1nj=1ngcd(i,j)×lcm(i,j) \sum_{i=1}^n\sum_{j=1}^ngcd(i,j)\times lcm(i,j)

很容易知道,這個可以這樣變換:

i=1nj=1ngcd(i,j)ijgcd(i,j)=i=1nj=1nij=(i=1ni)(j=1nj) =(i=1ni)2 \sum_{i=1}^n\sum_{j=1}^ngcd(i,j)\frac{ij}{gcd(i,j)} \\ =\sum_{i=1}^n\sum_{j=1}^nij \\ =\left(\sum_{i=1}^ni\right)\left(\sum_{j=1}^nj\right) \\\ \\ =\left(\sum_{i=1}^ni\right)^2

所以我們看,原來的式子是否也能這樣變換。

首先我們可以知道f=(μid)1f=(\mu\cdot id)*\mathbf 1(其中*為狄利克雷卷積)。

由於積性函式的乘積以及狄利克雷卷積仍舊為積性函式,所以ff也為積性函式。

那麼我們考慮,對於一對i,ji,j,我們根據唯一分解定理,假設:

i=pxax j=pyby i=\prod p_x^{a_x} \\\ \\ j=\prod p_y^{b_y}

那麼f(gcd(i,j))×f(lcm(i,j))f(gcd(i,j))\times f(lcm(i,j))可以寫成:

f(pwmin(aw,bw))f(pwmax(aw,bw)) \prod f(p_w^{min(a_w,b_w)}) \prod f(p_w^{max(a_w,b_w)})
(其中是因為積性函式f(xy)=f(x)f(y)f(xy)=f(x)f(y),在xyx\perp y時(互質),所以將i,ji,j拆開)

那麼我們將屬於ii的放在一起,屬於jj的放在一起,可以得到:

f(pxax)f(pyay)=f(i)f(j) \prod f(p_x^{a_x})\prod f(p_y^{a_y}) \\ =f(i)f(j)

所以就可以按照前面的例子的做法,將其分開變換後可以得到,原式等於:

(i=1nf(i))2 \left(\sum_{i=1}^n f(i)\right)^2

那麼現在我們考慮如何求ff的字首和,由於n109n\leq 10^9,所以要用杜教篩。

我們看,f=(μid)1f=(\mu\cdot id)*\mathbf 1,那麼我們令g=idg=id,則fgf*g就等於:

fg=(μid)1id=(μid)id1=(dnμ(d)d×nd)1=(ndnμ(d))1=(idϵ)1=ϵ1=1 \begin{aligned} f*g=&(\mu\cdot id)*\mathbf 1 * id \\ =&(\mu\cdot id)*id*\mathbf 1 \\ =&\left(\sum_{d|n}\mu(d)d\times \frac{n}{d}\right)*\mathbf 1 \\ =& \left(n\sum_{d|n}\mu(d)\right)*\mathbf 1 \\ =&(id\cdot \epsilon )*\mathbf 1 \\ =&\epsilon * \mathbf 1 \\ =& \mathbf 1 \end{aligned}

那麼我們將其帶入杜教篩的公式,可以得到字首和等於:

S(n)=i=1n1i=2niS(ni) S(n)=\sum_{i=1}^n 1-\sum_{i=2}^ni S(\lfloor\frac{n}{i}\rfloor)

我們預處理n23n^{\frac{2}{3}}的字首和,然後杜教篩就好啦。

關於ff的線性篩,方法如下:

因為當μ(pc)\mu(p^c)c>1c>1μ(pc)=0\mu(p^c)=0,所以原式為:

f(pc)=i=0cμ(pi)pi=1p f(p^c)=\sum_{i=0}^c\mu(p^i)p^i=1-p
根據積性函式定義:
f(pc)=1pf(x×pc)=f(pc)f(x)[xpc] f(p^c)=1-p \\ f(x\times p^c)=f(p^c)f(x)[x\perp p^c]

所以這個題就可以在O(n23)O(n^{\frac{2}{3}})時間內解決了。

#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=1e6+10;
const ll Mod=1e9+7;
ll prime[M],mu[M],cnt;
bool vis[M];
ll F[M],c[M],f[M],inv_2;
ll fpow(ll a,ll b){
	ll res=1;
	for(;b;b>>=1,a=(a*a)%Mod){
		if(b&1)res=(res*a)%Mod;
	}
	return res;
}
void init(){
	inv_2=fpow(2,Mod-2);
	F[1]=1;
	for(ll i=2;i<M;i++){
		if(!vis[i]){
			prime[++cnt]=i;
			F[i]=((1-i)%Mod+Mod)%Mod;
			c[i]=i;f[i]=i;
		}
		for(ll j=1,v;j<=cnt&&i*prime[j]<M;j++){
			v=i*prime[j];
			vis[v]=1;
			if(!(i%prime[j])){
				c[v]=c[i]*f[i];f[v]=f[i];
				F[v]=F[v/c[v]]*F[prime[j]]%Mod;
				break;
			}
			F[v]=F[i]*F[prime[j]]%Mod;
			c[v]=prime[j];f[v]=prime[j];
		}
	}
	for(ll i=2;i<M;i++){
		F[i]=(F[i-1]+F[i])%Mod;
	}
}
ll S(ll x){return ((x*(x+1))%Mod)*inv_2%Mod;}
ll Sarea(ll L,ll R){return ((S(R)-S(L-1))%Mod+Mod)%Mod;}
map <ll,ll> mp;
ll calc(ll x){
	if(x<M) return F[x];
	if(mp.count(x)) return mp[x];
	ll ans=x;
	for(ll i=2,j;i<=x;i=j+1){
		j=(x/(x/i));
		ans=(ans-(Sarea(i,j)*calc(x/i))%Mod)%Mod;
	}
	if(ans<0)ans+=Mod;
	return mp[x]=ans;
}
ll n;
ll Sqr(ll a){return a*a%Mod;}
int main(){
	scanf("%lld",&n);
	init();
	printf("%lld\n",Sqr(calc(n)));
	return 0;
}

End