完全平方数

完全平方数

https://ac.nowcoder.com/acm/problem/212437

完全平方数

题目大意

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

分析

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

此话怎讲?

先考虑的定义

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

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

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

容斥

就是说,要从简单的入手

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

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

那么,现在有这些素数

的贡献就有

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

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

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

那么回到这道题,就是说,一个数由个因子构成,那么他的贡献可以表示为(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);
    }
}

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

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

有的没的 文章被收录于专栏

RT,有的没的

全部评论

相关推荐

不愿透露姓名的神秘牛友
2025-12-17 16:48
今天九点半到公司,我跟往常一样先扫了眼电脑,屁活儿没有。寻思着没事干,就去蹲了个厕所,回来摸出手机刷了会儿。结果老板刚好路过,拍了我一下说上班别玩手机,我吓得赶紧揣兜里。也就过了四十分钟吧,我的直属领导把我叫到小隔间,上来就给我一句:“你玩手机这事儿把老板惹毛了,说白了,你可以重新找工作了,等下&nbsp;HR&nbsp;会来跟你谈。”&nbsp;我当时脑子直接宕机,一句话都没憋出来。后面&nbsp;HR&nbsp;找我谈话,直属领导也在旁边。HR&nbsp;说我这毛病不是一次两次了,属于屡教不改,不光上班玩手机,还用公司电脑看论文、弄学校的事儿。我当时人都傻了,上班摸鱼是不对,可我都是闲得发慌的时候才摸啊!而且玩手机这事儿,从来没人跟我说过后果这么严重,更没人告诉我在公司学个习也算犯错!连一次口头提醒都没有,哪儿来的屡教不改啊?更让我膈应的是,昨天部门刚开了会,说四个实习生里留一个转正,让大家好好表现。结果今天我就因为玩手机被开了。但搞笑的是,开会前直属领导就把我叫去小会议室,明明白白告诉我:“转正这事儿你就别想了,你的学历达不到我们部门要求,当初招你进来也没打算给你这个机会。”合着我没入贵厂的眼是吧?可我都已经被排除在转正名单外了,摸个鱼至于直接把我开了吗?真的太离谱了!
rush$0522:转正名单没进,大概率本来就没打算留你
摸鱼被leader发现了...
点赞 评论 收藏
分享
今天 07:38
已编辑
门头沟学院 Java
2.4&nbsp;一面2.6&nbsp;二面2.9&nbsp;三面(hr面)2.13&nbsp;oc1.15号收到面试电话那会就开始准备,因为一开始没底所以选择推迟一段时间面试,之后开始准备八股,准备实习可能会问的东西,这期间hot100过了有六七遍,真的是做吐了快,八股也是背了忘,忘了背,面经也看了很多,虽然最后用上的只有几道题,可是谁知道会问什么呢自从大二上开始学java以来,一路走来真的太痛了,一开始做外卖,点评,学微服务,大二下五六月时,开始投简历,哎,投了一千份了无音讯,开始怀疑自己(虽然能力确实很一般),后来去到一家小小厂,但是并不能学到什么东西,而且很多东西都很不规范,没待多久便离开,大二暑假基本上摆烂很怀疑自己,大三上因为某些原因开始继续学,期间也受到一俩个中小厂的offer,不过学校不知道为啥又不允许中小厂实习只允许大厂加上待遇不太好所以也没去,感觉自己后端能力很一般,于是便打算转战测开,学习了一些比较简单的测试理论(没有很深入的学),然后十二月又开始继续投,java和测开都投,不过好像并没有几个面试,有点打击不过并没有放弃心里还是想争一口气,一月初因为学校事比较多加上考试便有几天没有继续投,10号放假后便继续,想着放假应该很多人辞职可能机会大一点,直到接到字节的面试,心里挺激动的,总算有大厂面试了,虽然很开心,但同时压力也很大,心里真的很想很想很想进,一面前几天晚上都睡不好觉,基本上都是二三点睡六七点醒了,好在幸运终于眷顾我一次了(可能是之前太痛了),一面三十几分钟结束,问的都不太难,而且面试官人挺好但是有些问题问的很刁钻问到了测试的一些思想并不是理论,我不太了解这方面,但是也会给我讲一讲他的理解,但是面完很伤心觉得自己要挂了。但是幸运的是一面过了(感谢面试官),两天后二面,问的同样不算难,手撕也比较简单,但也有一两个没答出来,面试官人很好并没有追问,因为是周五进行的二面,没有立即出结果,等到周一才通知到过了,很煎熬的两天,根本睡不好,好在下周一终于通知二面过了(感谢面试官),然后约第二天三面,听别的字节同学说hr面基本上是谈薪资了,但是我的并不是,hr还问了业务相关的问题,不过问的比较浅,hr还问我好像比较紧张,而且hr明确说了还要比较一下,我说我有几家的面试都拒了就在等字节的面试(当然紧张,紧张到爆了要),三面完后就开始等结果,这几天干啥都没什么劲,等的好煎熬,终于13号下午接到了电话通知oc了,正式邮件也同时发了,接到以后真的不敢信,很激动但更重要的是可以松一口气了,可以安心的休息一下了终于可以带着个好消息过年了,找实习也可以稍微告一段落了,虽然本人很菜,但是感谢字节收留,成为忠诚的节孝子了因为问的比较简单,面经就挑几个记得的写一下一面:1.实习项目的难点说一下2.针对抖音评论设计一下测试用例3.手撕:合并两个有序数组二面:1.为什么转测开2.线程进程区别,什么场景适合用哪个3.发送一个朋友圈,从发出到别人看到,从数据流转的角度说一下会经历哪些过程4.针对抖音刷到广告视频设计测试用例5.手撕:无重复字符的最长字串
查看8道真题和解析
点赞 评论 收藏
分享
评论
3
4
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务