Sdoi2015概数个数和题解莫比乌斯反演
Sdoi2015约数个数和题解莫比乌斯反演
题目描述
T组数据,求
ΣNi=1ΣMj=1d(ij) ,d(x) 代表x的约数个数。1≤N,M,T≤105
题解
首先,膜拜一下PoPoQQQ大神及其题解
然后,有一个神奇的结论:
ΣNi=1ΣMj=1d(ij) =ΣNi=1ΣMj=1[Ni][Mj][gcd(i,j)=1]
要证上式,只需证d(nm)=Σi|nΣj|m[gcd(i,j)=1] ,因为上式即为该式的前缀和形式。
分开考虑每个质数p 对答案的贡献。设n=n′pk1,m=m′pk2 ,那p 对d(nm) 的贡献就是k1+k2+1 ,对等式右边的贡献是有序数对(i,j) 中的(pk1,1)(pk1−1,1)(pk1−2,1)…(1,1)(1,p)…(1,pk2−1)(1,pk2) 也是k1+k2+1 ,所以原式得证。再令
f(x)=Σxi=1[xi] ,显然有f(x)=d(x) ,因为f(x) 相当于把i=1→x 对d(x) 的贡献求和,就是d(x) 本身-
然后可以莫比乌斯反演了:
ans=ΣNi=1ΣMj=1[Ni][Mj][gcd(i,j)=1]=ΣNi=1ΣMj=1Σk|gcd(i,j)μ(k)[Ni][Mj]=ΣNk=1Σ[Nk]i=1Σ[Mk]j=1μ(k)[Nki][Mkj]=ΣNk=1μ(k)Σ[Nk]i=1[Nki]Σ[Mk]j=1[Nkj]=ΣNk=1μ(k)f([Mk])f([Mk])=ΣNk=1μ(k)d([Nk])d([Mk]) 然后用筛法筛出
μ(i) 和d(i) 再做就好了。看来还需要锻炼自己的数学功底啊
Code
#include <cstdio>
#include <algorithm>
#define ll long long
#define M 50000
using namespace std;
int mu[M + 5], n, m, pri[M], top, d[M + 5], f[M + 5];
bool mrk[M + 5];
void getmu()
{
mu[1] = d[1] = 1;
for(int i = 2; i <= M; ++i)
{
if(!mrk[i])
{
pri[top++] = i;
mu[i] = -1;
d[i] = 2;
f[i] = 1;
}
for(int j = 0; j < top && i * pri[j] <= M; ++j)
{
mrk[i * pri[j]] = true;
if(i % pri[j] == 0)
{
mu[i * pri[j]] = 0;
f[i * pri[j]] = f[i] + 1;
d[i * pri[j]] = d[i] / (f[i] + 1) * (f[i] + 2);
break;
}
mu[i * pri[j]] = -mu[i];
d[i * pri[j]] = (d[i] << 1);
f[i * pri[j]] = 1;
}
}
for(int i = 2; i <= M; ++ i)
{
d[i] += d[i - 1];
mu[i] += mu[i - 1];
}
}
inline ll work(int n, int m)
{
if(n > m) swap(n, m);
ll ans = 0;
for(int i = 1, j; i <= n; i = j + 1)
{
j = min(n / (n / i), m / (m / i));
ans += (ll)(mu[j] - mu[i - 1]) * (ll)d[n / i] * (ll)d[m / i];
}
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("d.txt", "r", stdin);
freopen("a.txt", "w", stdout);
#endif
int T;
getmu();
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
printf("%lld\n", work(n, m));
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。