Luogu P3327 [SDOI2015]约数个数和 题解

题目传送门

题目大意

\[\sum_{i=1}^n\sum_{j=1}^m d(ij) \]

其中 \(n,m,T\le 5\cdot 10^4\),多组测试数据,\(T\) 为测试组数。

题目解析

众所周知,考虑莫比乌斯反演的时候,需要出现 \(\gcd\),然后枚举 \(\gcd\),展开 \([x=1]\)
所以我们考虑咋么出现 \(\gcd\)
根据 人类智慧,我们可以得到约数函数 \(d\) 具有如下性质:

\[\begin{aligned} & d(ij) \\ = & \sum_{x|i}\sum_{y|j}[\gcd(x,y)=1]\\ = & \sum_{x|i}\sum_{y|j}\sum_{k|x,k|y}\mu(k)\\ = & \sum_{k|i,k|j}\mu(k)\sum_{k|x,x|i}\sum_{k|y,y|j}1\\ = & \sum_{k|i,k|j}\mu(k)d\left(\dfrac{i}{k}\right)d\left(\dfrac{j}{k}\right) \end{aligned} \]

这样的好处就是,这里出现了 \(\dfrac{i}{k}\),所以最后我们枚举 \(\dfrac{i}{k}\) 的时候,约数函数里面就不会有最外面枚举的 \(k\) 了。
然后就是套路了,直接回带。

\[\begin{aligned} & \sum_{i=1}^n\sum_{j=1}^md(i,j)\\ = & \sum_{i=1}^n\sum_{j=1}^m\sum_{k|i,k|j}\mu(k)d\left(\dfrac{i}{k}\right)d\left(\dfrac{j}{k}\right)\\ = & \sum_{k=1}^{\min\{n,m\}}\mu(k)\sum_{k|i,i\le n}\sum_{k|j,j\le m} d\left(\dfrac{i}{k}\right)d\left(\dfrac{j}{k}\right)\\ = & \sum_{k=1}^{\min\{n,m\}}\mu(k)\sum_{i=1}^{\lfloor n/k\rfloor}\sum_{j=1}^{\lfloor m/k\rfloor}d(i)d(j)\\ = & \sum_{k=1}^{\min\{n,m\}}\mu(k) S(\lfloor n/k \rfloor)S(\lfloor m/k \rfloor) \end{aligned} \]

其中

\[S(n)=\sum_{i=1}^nd(i) \]

线性筛+整除分块,\(\Theta(n+T\sqrt n)\)

其实可以算是多余的代码:

#include<cstdio>
#define db double
#define gc getchar
#define pc putchar
#define U unsigned
#define ll long long
#define ld long double
#define ull unsigned long long
#define Tp template<typename _T>
#define Me(a,b) memset(a,b,sizeof(a))
Tp _T mabs(_T a){ return a>0?a:-a; }
Tp _T mmax(_T a,_T b){ return a>b?a:b; }
Tp _T mmin(_T a,_T b){ return a<b?a:b; }
Tp void mswap(_T &a,_T &b){ _T tmp=a; a=b; b=tmp; return; }
Tp void print(_T x){ if(x<0) pc('-'),x=-x; if(x>9) print(x/10); pc((x%10)+48); return; }
#define EPS (1e-7)
#define INF (0x7fffffff)
#define LL_INF (0x7fffffffffffffff)
#define N 50000
#define maxn 50039
#define maxm
#define MOD
#define Type int
#ifndef ONLINE_JUDGE
//#define debug
#endif
using namespace std;
Type read(){
	char c=gc(); Type s=0; int flag=0;
	while((c<'0'||c>'9')&&c!='-') c=gc(); if(c=='-') c=gc(),flag=1;
	while('0'<=c&&c<='9'){ s=(s<<1)+(s<<3)+(c^48); c=gc(); }
	if(flag) return -s; return s;
}
int n,m,isp[maxn],pr[maxn],cnt; ll mu[maxn],d[maxn],g[maxn];
void init(){
	int i,j; d[1]=1; mu[1]=1;
	for(i=2;i<=N;i++){
		if(!isp[i]) pr[++cnt]=i,d[i]=2,mu[i]=-1,g[i]=1;
		for(j=1;j<=cnt&&i*pr[j]<=N;j++){
			isp[i*pr[j]]=1; mu[i*pr[j]]=-mu[i]; d[i*pr[j]]=d[i]*2; g[i*pr[j]]=1;
			if(i%pr[j]==0){ mu[i*pr[j]]=0; g[i*pr[j]]=g[i]+1; d[i*pr[j]]=d[i]/(g[i]+1)*(g[i]+2); break; }
		}
	}
	for(i=1;i<=N;i++) mu[i]+=mu[i-1],d[i]+=d[i-1];
}
void work(){
	n=read(); m=read(); int l=1,r; ll ans=0;
	while(l<=n&&l<=m){
		r=mmin(n/(n/l),m/(m/l));
		ans+=1ll*(mu[r]-mu[l-1])*d[n/l]*d[m/l];
		l=r+1;
	} print(ans),pc('\n');
}
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	init(); int T=read(); while(T--) work(); return 0;
}
posted @ 2023-03-02 20:05  jiangtaizhe001  阅读(13)  评论(1编辑  收藏  举报