P4619 [SDOI2018] 旧试题 - 数论

题解

毒瘤。

根据一个结论 \(\mathrm{d}(xyz)=\sum_{a\mid x,b\mid y,c\mid z} [a\perp b][b\perp c][a\perp c]\),可以得知:

\[\begin{aligned} &\sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C \mathrm{d}(ijk)\\ &=\sum_{i,j,k} \lfloor \frac{A}{i}\rfloor \lfloor \frac{B}{i}\rfloor \lfloor \frac{C}{i}\rfloor [i\perp j][j\perp k][i\perp k] \end{aligned} \]

我们选择其中的 \([i\perp j]\) 反演。记 \(f(n,k)=\sum_{i=1}^n [i\perp k]\lfloor \frac{n}{i}\rfloor\),得到

\[\sum_k \lfloor \frac{C}{k}\rfloor\sum_d [d\perp k]\mu(d) f(\lfloor \frac{A}{d}\rfloor,k)f(\lfloor \frac{B}{d}\rfloor,k) \]

后面那个是一个二维整除分块的形式。设 \(g(n,k)\sum_{i=1}^n [i\perp k]\mu(i)\),假如我们能 \(\mathcal{O}(x)\) 算出 \(f,g\) 的单点值,我们就可以枚举 \(k\) 并整除分块,做到 \(\mathcal{O}(nx\sqrt{n})\) 的复杂度。

显然 \(k\) 对于 \(f,g\) 的影响只有互质,设 \(k=\prod_i p_i^{e_i}\)(其中 \(p_i\) 是第 \(i\) 个质数),那么我们可以令 \(k\gets \prod_i p_i^{\min(e_i,1)}\),这样的更改对答案没有任何影响。

于是我们只需要考虑 \(f(n,k),g(n,k)\) 在 \(k\) 是无平方因子数处的取值。设 \(p\) 是 \(k\) 的一个因子。

先考虑 \(f\):

\[\begin{aligned} f(n,k)&=f(n,\frac{k}{p})-\sum_{i} [i\perp \frac{k}{p}][i\not\perp p]\lfloor \frac{n}{i}\rfloor\\ &=f(n,\frac{k}{p})-\sum_i [i\perp \frac{k}{p}][p\mid i]\lfloor \frac{n}{i}\rfloor\\ &=f(n,\frac{k}{p})-\sum_i^{n/p} [pi\perp \frac{k}{p}]\lfloor \frac{n}{pi}\rfloor\\ &=f(n,\frac{k}{p})-\sum_i^{n/p} [i\perp \frac{k}{p}]\lfloor \frac{n}{pi}\rfloor\\ &=f(n,\frac{k}{p})-f(\lfloor\frac{n}{p}\rfloor,\frac{k}{p}) \end{aligned} \]

显然,若 \(n\) 在整除分块时被取到,则 \(\lfloor\frac{n}{p}\rfloor\) 也会被取到。所以这种做法计算 \(f\) 是 \(\mathcal{O}(1)\) 的。

同理,应用结论 \(\mu(xy)=\mu(x)\mu(y)[x\perp y]\),可以得到:

\[g(n,k)=g(n,\frac{k}{p})+g(\lfloor \frac{n}{p}\rfloor,k) \]

这样就得到了一种 \(\mathcal{O}(n\sqrt{n})\) 的超大常数做法(超大常数是因为你要写两个哈希表)。

但它跑得太慢了!

我们观察 \(f,g\) 会取哪些值。这是一个经典的整除分块:

for(int l=1,r;l<=min(a,b);l=r+1){
	r=min(a/(a/l),b/(b/l));
	do something with f(a/l,...),f(b/l,...) and g(r,...)-g(l-1,...);
}

\(f\) 的值会取在整除分块能取的值的位置;\(g\) 的值会取在整除分块的分界线处。

所以,我们可以 dfs 找所有无平方因子数,并且用 \(\log n\) 个数组存所有历史的 \(f\)。

注意整除分块的上界不是 \(\min(a,b)\) 而是 \(\max(a,b)\)。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
#define Debug(...) fprintf(stderr,__VA_ARGS__)
typedef long long ll;
const int N=1e5+5,LogN=20,Mod=1e9+7;
int T,a,b,c;
int prime[N],pcnt,npr[N],num[N],mu[N];
void Sieve(int mx){
	npr[1]=num[1]=mu[1]=1;
	For(i,2,mx){
		if(!npr[i]) prime[++pcnt]=i,num[i]=i,mu[i]=Mod-1;
		for(int j=1;j<=pcnt&&i*prime[j]<=mx;++j){
			npr[i*prime[j]]=1;
			if(i%prime[j]) mu[i*prime[j]]=(-mu[i]+Mod)%Mod,num[i*prime[j]]=num[i]*prime[j];
			else{mu[i*prime[j]]=0,num[i*prime[j]]=num[i];break;}
		}
	}
}
ll val[N],f[N][LogN],g[N][LogN],ans;
void Update(int x,int k){
	ll res=0;
	for(int l=1,r;l<=min(a,b);l=r+1){
		r=min(a/(a/l),b/(b/l));
		(res+=f[a/l][k]*f[b/l][k]%Mod*(g[r][k]-g[l-1][k]+Mod))%=Mod;
	}
	(ans+=val[x]*res)%=Mod;
}
void Dfs(int cur,ll x,int k){
	Update(x,k);
	for(int p=cur;p<=pcnt&&1LL*prime[p]*x<=c;++p){
		for(int l=1,r;l<=min(a,b);l=r+1){
			r=min(a/(a/l),b/(b/l));
			f[a/l][k+1]=(f[a/l][k]-f[a/l/prime[p]][k]+Mod)%Mod;
			f[b/l][k+1]=(f[b/l][k]-f[b/l/prime[p]][k]+Mod)%Mod;
			g[r][k+1]=(g[r][k]+g[r/prime[p]][k+1])%Mod;
		}
		Dfs(p+1,x*prime[p],k+1);
	}
}
int main(){
	#ifndef zyz
	ios::sync_with_stdio(false),cin.tie(nullptr);
	#endif
	cin>>T;
	Sieve(N-5);
	while(T--){
		memset(val,0,sizeof val);
		cin>>a>>b>>c;
		ans=0;
		For(i,1,c){
			(val[num[i]]+=c/i)%=Mod;
		}
		For(i,1,max(a,b)){
			f[i][1]=0;
			for(int l=1,r;l<=i;l=r+1){
				r=i/(i/l);
				(f[i][1]+=1LL*(r-l+1)*(i/l))%=Mod;
			}
			g[i][1]=(g[i-1][1]+mu[i]+Mod)%Mod;
		}
		Dfs(1,1,1);
		cout<<ans<<endl;
	}
	return 0;
}
上一篇:【数学】数论分块


下一篇:征信区块链解决方案探索(Hyperledger)