3994: [SDOI2015]约数个数和
Time Limit: 20 Sec Memory Limit:128 MBSubmit: 239 Solved: 176
[ Submit][ Status][ Discuss]
Description
Input
输入文件包含多组测试数据。
Output
T行,每行一个整数,表示你所求的答案。
Sample Input
7 4
5 6
Sample Output
121
HINT
1<=N, M<=50000
Source
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3994
题目分析:和上一题类似,还是见公式:
∑ni=1∑mj=1d(ij)=∑ni=1∑mj=1⌊ni⌋⌊mj⌋[gcd(i,j)==1]
,
于是得到:
变形得到
∑d=1min(n,m)μ(d)∑i=1n/d∑j=1m/d⌊nid⌋⌊mjd⌋
继续变形
令
公式变成
现在的问题是怎样计算f的值,f[i]表示的是 ∑ik=1⌊ik⌋
,我们先考虑一个函数f'[i],设f'[i]为i的约数的个数,那么显然f'[i]为积性函数,我们可以用线性筛得到,仔细观察可以发现f[i] = f'[1] + f'[2] + ... + f'[i],比如f[6] = f'[1]+f'[2]+...+f'[6] = 1+2+2+3+2+4 = 6+3+2+1+1+1 = 14,下面简单解释一下线性筛是如何得到一个数约数的个数的,首先根据约数个数定理:对于一个大于1的正整数n可以分解质因数:n=p1^a1*p2^a2*p3^a3*…*pk^ak,则n的正约数有(a₁+1)(a₂+1)(a₃+1)…(ak+1)个,又我们知道线性筛筛质数每次筛的都是用最小的质因子去筛,因此我们可以记录这个最小质因子唯一分解后的次幂然后通过上面的公式求解,详细见程序注释。这样的话问题就解决了,同时把u和f都筛出来,剩下的分块求和即可
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=50000+5;
const int mod=1073741824;
typedef long long ll;
int mb[N];
int pri[N],tot=0;
bool vis[N];
ll f[N];
int d[N];
int ff[N];
ll sum[N];
void init()
{
memset(vis,false,sizeof vis);
mb[1]=1;
sum[0]=0;
sum[1]=1;
ff[1]=1;
f[1]=1;
for(int i=2;i<N;i++)
{
if(!vis[i]) {
pri[tot++]=i;
mb[i]=-1;
d[i]=1;
ff[i]=2;
}
for(int j=0;j<tot && i*pri[j]<N;j++)
{
vis[i*pri[j]]=true;
if(i%pri[j]) {
mb[i*pri[j]]=-mb[i];
ff[i*pri[j]]=ff[i]*2;
d[i*pri[j]]=1;
}
else {
mb[i*pri[j]]=0;
ff[i*pri[j]]=ff[i]/(d[i]+1)*(d[i]+2);
d[i*pri[j]]=d[i]+1;
break;
}
}
sum[i]=sum[i-1]+mb[i];
f[i]=f[i-1]+ff[i];
}
}
int main()
{
init();
int T_T;
scanf("%d",&T_T);
while(T_T--)
{
int n,m;
scanf("%d%d",&n,&m);
ll ans=0;
int up=min(n,m);
for(int d=1,la=0;d<=up;d=la+1)
{
la=min(n/(n/d),m/(m/d));
ans+=(sum[la]-sum[d-1])*f[n/d]*f[m/d];
}
printf("%lld\n",ans);
}
return 0;
}