完全平方数

题目大意

就是请你求第无平方因字数

分析

有感觉的同学会发现,这个似乎和有关

此话怎讲?

先考虑的定义

那么显然可以得到,无平方因子数的个数可以有如下表示

即所有无平方因字数的贡献都为,而其他数没有贡献

那么从这里开始就可以有两种做法了

容斥

就是说,要从简单的入手

去减掉带有平方因子数的个数

那么就可以来枚举一下每个完全平方数对答案的贡献

那么,现在有这些素数

的贡献就有

但是例如这个数它其实是被计算了两次的

多枚举几个数以后,会发现由偶数个素数构成的数会被重复计算,那么重复的部分是需要减去的

如图,红绿蓝三个交集再三个大圆中被重复计算了,中间那个像勒洛三角形的部分再减去原交集的时候同时也被剪掉了,所以又要加上它的贡献

那么回到这道题,就是说,一个数由个因子构成,那么他的贡献可以表示为(k & 1) ? -1 : 1

那么这不就变回了莫比乌斯函数了吗?

所以可以得到:

就可以直接写了

用二分查找这个应该就不用说了吧

Code(100)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int Maxn = 5e4;
int T, A, B;
int P[Maxn + 10], Cnt;
int mu[Maxn + 10];
bool Vis[Maxn + 10];

inline void Init()
{
    mu[1] = 1;
    for (int i = 2; i <= Maxn; ++i)
    {
        if (!Vis[i]) P[++Cnt] = i, mu[i] = -1;
        for (int j = 1; j <= Cnt && i * P[j] <= Maxn; ++j)
        {
            Vis[i * P[j]] = 1;
            if (i % P[j]) mu[i * P[j]] = -mu[i];
            else break;
        }
    }
}

bool Check(int x)
{
    int ans(x);
    for (ll i = 2, r; i * i <= x; ++i)
        ans += mu[i] * (x / i / i);
    return ans >= A;
}

inline void Find(int X)
{
    ll l(X), r(X << 1), ans(0);
    while (l <= r)
    {
        ll Mid((l + r) >> 1);
        if (Check(Mid)) ans = Mid, r = Mid - 1;
        else l = Mid + 1;
    }
    printf ("%lld\n", ans);
}

int main()
{
    Init();
    scanf ("%d", &T);
    while (T--)
    {
        scanf ("%d", &A);
        Find(A);
    }
}

这个时间复杂度是

理论上乱写都是可以过的

但是还是可以在稍稍优化一下

那么就要考虑对这个求和进行类整数分块

可以证明的取值是连续的

那么加上这样一个分块,这道题就可以过了

Code(100)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int Maxn = 5e4;
int T, A, B;
int P[Maxn + 10], Cnt;
int mu[Maxn + 10];
bool Vis[Maxn + 10];

inline void Init()
{
    mu[1] = 1;
    for (int i = 2; i <= Maxn; ++i)
    {
        if (!Vis[i]) P[++Cnt] = i, mu[i] = -1;
        for (int j = 1; j <= Cnt && i * P[j] <= Maxn; ++j)
        {
            Vis[i * P[j]] = 1;
            if (i % P[j]) mu[i * P[j]] = -mu[i];
            else break;
        }
    }
    for (int i = 1; i <= Maxn; ++i) mu[i] += mu[i - 1];
}

bool Check(int x)
{
    ll ans(x);
    for (int l = 2, r; l * l <= x; l = r + 1) {
        r = min(sqrt(x / (x / l / l)), sqrt(x));
        ans += (mu[r] - mu[l - 1]) * (x / l / l);
    }
    return ans >= A;
}

inline void Find(int X)
{
    ll l(X), r(X << 1), ans(0);
    while (l <= r)
    {
        ll Mid((l + r) >> 1);
        if (Check(Mid)) ans = Mid, r = Mid - 1;
        else l = Mid + 1;
    }
    printf ("%lld\n", ans);
}

int main()
{
    Init();
    scanf ("%d", &T);
    while (T--)
    {
        scanf ("%d", &A);
        Find(A);
    }
}

杜教筛

其实我们发现也是一个不完全积性函数

那么就是可以考虑使用杜教筛的

杜教筛就不细说了,需要了解杜教筛的可以看我的这篇博客

考虑构造函数

使得能够方便的表示

能够方便表示的函数可以想到有:

那么显然是不好的得到的

那就考虑的构造(其实可以直接,不需要怎么构造别的函数)

那么就是说要让:

考虑有贡献时,当且仅到为无平方因子数,此时要让也有贡献,整体贡献才可能为

那么感觉一下,大概是一个与完全平方数有关的函数

那么定义,即 是否是完全平方数

那么对于就是成立的了,简单证明一下:

要让当且仅当为无平方因字数且为完全平方数时才有贡献

那么分两种情况考虑:

为无平方因子数:那么显然当不为的函数值都为,那么只有时,

不为无平方因子数:就是只有当的最大平方因子时,才是无平方因子数,也是完全平方数,此时才有的贡献

所以证明了这个构造是没有问题的

接下来,就是杜教筛的套路:

考虑到函数的性质,会发现只有当为完全平方数时,才有贡献

那么这个又可以写成:

int MMu(int x)
{
    if (x <= Maxn) return mu[x];
    if (Mu[x]) return Mu[x];
    int Ans(0);
    for (int l(2); 1ll * l * l <= x; ++l) Ans += MMu(x / (l * l));
    return Mu[x] = x - Ans;
}

就是一个样子的,最后套上二分,这道题就愉快的结束了

Code(100)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
unordered_map <int, int> Mu;
const int Maxn = 1e6;
int T, A, B;
int P[Maxn + 10], Cnt;
int mu[Maxn + 10];
bool Vis[Maxn + 10];

inline void Init()
{
    mu[1] = 1;
    for (int i = 2; i <= Maxn; ++i)
    {
        if (!Vis[i]) P[++Cnt] = i, mu[i] = 1;
        for (int j = 1; j <= Cnt && 1ll * i * P[j] <= Maxn; ++j)
        {
            Vis[i * P[j]] = 1;
            if (i % P[j]) mu[i * P[j]] = mu[i];
            else break;
        }
        mu[i] += mu[i - 1];
    }
}

int MMu(int x)
{
    if (x <= Maxn) return mu[x];
    if (Mu[x]) return Mu[x];
    int Ans(0);
    for (int l(2); 1ll * l * l <= x; ++l) Ans += MMu(x / (l * l));
    return Mu[x] = x - Ans;
}

inline void Find(int X)
{
    ll l(X), r(X << 1);
    while (l < r)
    {
        ll Mid((l + r) >> 1);
        if (MMu(Mid) >= X) r = Mid;
        else l = Mid + 1;
    }
    printf ("%lld\n", r);
}

int main()
{
    Init();
    scanf ("%d", &T);
    while (T--)
    {
        scanf ("%d", &A);
        Find(A);
    }
}

有时间的话,再用筛写一次吧

注意:不用开的地方就别开,不明白为什么有的连这个都要卡