P4619题解
一道智慧的反演题
好像不用三元图,纯数论也能过,但跑得有点小慢
前置芝士
1.莫比乌斯反演
2.整除分块
3.可以先做P3327
正题
众所周知,本题要我们求这个
那么对于
所以求这个
该枚举约树为枚举倍数
然后我们挑一个
以上我们走出了新手村,接下来的推导相当重要
我们令
其中
接下来,我们考虑优化
重要性质:设
那么对于
以上递推式其实也是数论体中的常见套路,可以用整除分块
再用上一个小深搜就可以 AC 了
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define reg register
const int N=2*1e5;
const int mod=1e9+7;
int vis[N],mul[N],d[N],saw[N],p[N],tot;
ll f[N][20],g[N][20],s[N],ans;
int T,A,B,C;
inline void ph(){
mul[1]=1;
d[1]=1;
saw[1]=1;
for(reg int i=2;i<N;i++){
if(!vis[i]){
p[++tot]=i;
mul[i]=-1;
d[i]=2;
saw[i]=i;
}
for(reg int j=1;j<=tot&&p[j]*i<N;j++){
vis[i*p[j]]=1;
if(i%p[j]==0){
d[i*p[j]]=(d[i]<<1)-d[i/p[j]];
saw[i*p[j]]=saw[i];
break;
}
d[i*p[j]]=d[i]<<1;
saw[i*p[j]]=saw[i]*p[j];
mul[i*p[j]]=-mul[i];
}
}
for(reg int i=1;i<N;i++){
d[i]+=d[i-1];
mul[i]+=mul[i-1];
}
// for(int i=1;i<=100;i++){
// printf("d--->%d mul---->%d\n",d[i],mul[i]);
// }
}
inline void kasu(){
g[0][1]=0;
f[0][1]=0;
for(reg int l=1,r;l<=A;++l){
r=min(A/(A/l),B/(B/l));
g[r][1]=mul[r];
f[A/l][1]=d[A/l];
f[B/l][1]=d[B/l];
l=r;
}
memset(s,0,sizeof(s));
for(reg int i=1;i<=C;i++){
s[saw[i]]+=C/i;
}
}
inline void reset(int x,int k){
for(reg int l=1,r;l<=A;++l){
r=min(A/(A/l),B/(B/l));
g[r][k]=g[r][k-1]+g[r/x][k];
f[A/l][k]=f[A/l][k-1]-f[(A/l)/x][k-1];
f[B/l][k]=f[B/l][k-1]-f[(B/l)/x][k-1];
l=r;
}
}
inline void dfs(int re,int x,int k){
ll res=0;
for(reg int l=1,r;l<=A;++l){
r=min(A/(A/l),B/(B/l));
res+=((g[r][k]-g[l-1][k])*f[A/l][k]*f[B/l][k])%mod;
res=(res%mod+mod)%mod;
l=r;
}
ans+=(s[re]*res)%mod;
ans%=mod;
for(reg int i=x;1ll*p[i]*re<=C;i++){
reset(p[i],k+1);
dfs(re*p[i],i+1,k+1);
}
return ;
}
int main(){
ios::sync_with_stdio(false);
cin>>T;
ph();
while(T--){
ans=0;
cin>>A>>B>>C;
if(A>B) swap(A,B);
kasu();
dfs(1,1,1);
cout<<ans<<endl;
}
return 0;
}