P3327 [SDOI2015]约数个数和
思考过程:
如果两个数互质:\(Ans=d[i]*d[j]\)。
我们可以枚举最大公约数。那么该如何将\(gcd\)的贡献加入?
我们换个思路,我们可以发现i和j的公共约数就是\(gcd(i,j)\)的约数.
那么\(d(ij)=d(i)+d(j)-d(gcd(i,j))\)
\[Ans=\sum_{i=1}^{n}d(i)*m+\sum_{i=1}^{m}d(j)*n-\sum_{i=1}^{n}\sum_{j=1}^{m}d(gcd(i,j))
\]
这时题目转化成一个熟悉的问题,那么我们可以枚举\(gcd\),计算出现的次数即可。
\[\sum_{i=1}^{min(n,m)}d[i]*Getans(n/i,m/i)
\]
\[Getans(n,m)=\sum_{i=1}^{min(n,m)}\mu(i)\lfloor n/i\rfloor \lfloor m/i\rfloor
\]
由于要用两次数位分块,时间复杂度到了\(O(nT)\)水平,还是无法A掉此题。
其实上面的式子推错了。/kk
题解思路:
\[d(ij)=\sum_{x|i}\sum_{y|j}[\gcd(x,y)]
\]
给个证明:
不妨将每个质因子分开来考虑,那么假设\(i\)有\(a\)个\(p\),\(j\)有\(b\)个\(p\),那么在上面的式子中会有\(a+b+1\)种选法。又因为每个质因子是互相独立的,证毕。
\[d(ij)=\sum_{x|i}\sum_{y|j}\sum_{p|gcd(x,y)}\mu(p)
\]
\[=\sum_{p=1}^{min(x,y)}\mu(p)\sum_{x|i}\sum_{y|j}[p|\gcd(x,y)]
\]
\[=\sum_{p=1}^{min(i,j)}\mu(p)\sum_{x|i/p}\sum_{y|j/p}1
\]
\[=\sum_{p|i,p|j}\mu(p)d[i/p]d[j/p]
\]
现在应该将上面的式子带入原式。
\[\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{p|i,p|j}\mu(p)d[i/p]d[j/p]
=\sum_{p=1}^{min(n,m)}\sum_{i=1}^{\lfloor n/p\rfloor}\sum_{j=1}^{\lfloor m/p\rfloor}\mu(p)d[i]d[j]
\]
终于完事了。
#include<bits/stdc++.h>
using namespace std;
bool f1;
#define Max(a,b) ((a)<(b))&&((a)=(b))
#define Min(a,b) ((a)>(b))&&((a)=(b))
#define LL long long
inline LL rd() {
LL res=0;bool f=0;
char ch;
while(ch=getchar(),ch<48||ch>57)if(ch=='-')f=true;
do res=(res<<1)+(res<<3)+(ch^48);
while(ch=getchar(),ch>47&&ch<58);
return f?-res:res;
}
const int M=1e5+5;
const int Mx=5e4;
int prime[M],cnt;
int d[M],Cnt[M],sum[M];
bool vis[M];
LL sumd[M];
void Pre() {
d[1]=sum[1]=1;
for(int i=2; i<=Mx; ++i) {
if(!vis[i])prime[++cnt]=i,d[i]=Cnt[i]=2,sum[i]=-1;
for(int j=1; j<=cnt&&prime[j]*i<=Mx; ++j) {
vis[prime[j]*i]=true;
if(i%prime[j]==0) {
d[i*prime[j]]=d[i]/Cnt[i]*(Cnt[i]+1);
Cnt[prime[j]*i]=Cnt[i]+1;
sum[prime[j]*i]=0;
break;
}else d[i*prime[j]]=d[i]*2,Cnt[i*prime[j]]=2,sum[i*prime[j]]=-sum[i];
}
}
for(int i=1; i<=Mx; ++i)sum[i]+=sum[i-1];
for(int i=1; i<=Mx; ++i)sumd[i]=sumd[i-1]+d[i];
}
int T;
bool f2;
int main(){
// printf("%lf\n",(&f2-&f1)/1024.0/1024.0);
// freopen("a.in","r",stdin);
// freopen("a.ans","w",stdout);
T=rd();Pre();
while(T--) {
int n=rd(),m=rd(),r;
LL ans=0;
if(n>m)swap(n,m);
for(int l=1; l<=n; l=r+1) {
r=min(n/(n/l),m/(m/l));
ans+=sumd[n/l]*sumd[m/l]*(sum[r]-sum[l-1]);
}
printf("%lld\n",ans);
}
return 0;
}

浙公网安备 33010602011771号