完全平方数
题目大意
就是请你求第无平方因字数
分析
有感觉的同学会发现,这个似乎和有关
此话怎讲?
先考虑的定义
那么显然可以得到,无平方因子数的个数可以有如下表示
即所有无平方因字数的贡献都为,而其他数没有贡献
那么从这里开始就可以有两种做法了
容斥
就是说,要从简单的入手
用去减掉带有平方因子数的个数
那么就可以来枚举一下每个完全平方数对答案的贡献
那么,现在有这些素数
的贡献就有
但是例如这个数它其实是被计算了两次的
多枚举几个数以后,会发现由偶数个素数构成的数会被重复计算,那么重复的部分是需要减去的
如图,红绿蓝三个交集再三个大圆中被重复计算了,中间那个像勒洛三角形的部分再减去原交集的时候同时也被剪掉了,所以又要加上它的贡献
那么回到这道题,就是说,一个数由个因子构成,那么他的贡献可以表示为(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); } }
有时间的话,再用筛写一次吧
注意:不用开的地方就别开,不明白为什么有的连这个都要卡