P4619 [SDOI2018] 旧试题 - 数论
题解
毒瘤。
根据一个结论 \(\mathrm{d}(xyz)=\sum_{a\mid x,b\mid y,c\mid z} [a\perp b][b\perp c][a\perp c]\),可以得知:
我们选择其中的 \([i\perp j]\) 反演。记 \(f(n,k)=\sum_{i=1}^n [i\perp k]\lfloor \frac{n}{i}\rfloor\),得到
后面那个是一个二维整除分块的形式。设 \(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\):
显然,若 \(n\) 在整除分块时被取到,则 \(\lfloor\frac{n}{p}\rfloor\) 也会被取到。所以这种做法计算 \(f\) 是 \(\mathcal{O}(1)\) 的。
同理,应用结论 \(\mu(xy)=\mu(x)\mu(y)[x\perp y]\),可以得到:
这样就得到了一种 \(\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;
}