DIVCNT2 - Counting Divisors (square)
\(\text{Problem}:\)DIVCNT2 - Counting Divisors (square)
\(\text{Solution}:\)
事实上给出的 \(\sum\limits_{i=1}^{n}d(i^{2})\) 有很多等价表达式,如以下:
解释一下其等价性。对于第 \(1\) 个式子,将 \(\text{lcm}\) 展开为 \(\gcd(i,j)=1\) 形式再代入即可;对于第 \(2\) 个式子,考虑展开 \(d^2(\frac{i}{j})\),有:
令 \(p=xj,q=yj\),则 \(\gcd(p,q)\geq j\)。易知所有 \(j\leq \gcd(p,q)\) 都产生贡献,故原式等于:
回到本题,首先大力展开 \(d(i^2)\)。
引理 \(1\):
利用唯一分解定理可证(大概是考虑每个质因子的贡献)。
将其代入原式,得到:
改变枚举顺序,计算 \(i\) 对每对 \((x,y)\) 产生的贡献,有:
发现 \(xy\leq n\) 的限制非常高明,于是枚举 \(d=xy\),有:
不难发现 \(g\leq \sqrt{n}\),故再次改变枚举顺序,将 \(g\) 提取到最前面。考虑 \(g\mid \gcd(x,\frac{d}{x})\) 等价于 \(g\mid x,gx\mid d\),故可以将 \(x\) 写为 \(kg\) 的形式,即 \(kg^{2}\mid d\),于是有:
发现 \(k,d\) 只在做乘积时出现,而枚举 \(d\) 的上标中又有 \(k\),故可以把 \(k\) 提取到下面,变为枚举 \(s=kd\),同时考虑多少组 \((k,d)\) 会贡献给 \(s\),得到:
至此,暴力枚举 \(g\),对第二层 \(\sum_{s}\) 做整除分块即可。现在问题转化为求出因数个数函数 \(d\) 的前缀和。根据定义式,即满足 \(ij\leq x\) 的有序对 \((i,j)\) 的数量,有:
发现 \(d(s)\) 也可以整除分块求解。由于多组数据,可以线性筛出较小的 \(\sum\limits_{i=1}^{n}d(i)\),对于较大的,计算完后用 \(map\) 存储答案,下次可以快速调用。这种做法可以通过本题。
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define int unsigned long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=30000010;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int pri[N/10],cnt,mu[N],d[N],e[N];
bool book[N];
inline void Init()
{
mu[1]=d[1]=1;
for(ri int i=2;i<N;i++)
{
if(!book[i]) pri[++cnt]=i, mu[i]=-1, e[i]=1, d[i]=2;
for(ri int j=1;j<=cnt&&i*pri[j]<N;j++)
{
book[i*pri[j]]=1;
if(i%pri[j]==0)
{
e[i*pri[j]]=e[i]+1;
d[i*pri[j]]=d[i]/e[i*pri[j]]*(e[i*pri[j]]+1);
break;
}
else mu[i*pri[j]]=-mu[i], e[i*pri[j]]=1, d[i*pri[j]]=d[i]*2;
}
}
for(ri int i=1;i<N;i++) d[i]+=d[i-1];
}
map<int,int> Q;
inline int Sig(int x)
{
if(x<N) return d[x];
if(Q.find(x)!=Q.end()) return Q[x];
int res=0;
for(ri int l=1,r;l<=x;l=r+1)
{
r=x/(x/l);
res+=(x/l)*(r-l+1);
}
return Q[x]=res;
}
signed main()
{
Init();
for(ri int T=read();T;T--)
{
int n=read();
int ans=0;
for(ri int j=1;j*j<=n;j++)
{
if(!mu[j]) continue;
int res=0;
int m=n/(j*j);
int lst=0,now=0;
for(ri int l=1,r;l<=m;l=r+1,lst=now)
{
r=m/(m/l);
now=Sig(r);
res+=(m/l)*(now-lst);
}
ans+=res*mu[j];
}
printf("%llu\n",ans);
}
return 0;
}