这个题,可以设Ai表示含有pi的平方的数的集合,
那么所有的不为素数平方的数的集合个数就是Ai取反后的交集所形成的集合的元素个数。
根据这个,可以推导。p1=2,那么A1的个数就是n/(22) ,p2=3,A2的个数就是n/(33),A1与A2的交集的个数则为n/(2*3)^2 ,根据容斥原理的公式,A1,A2应该被减去,A1与A2的交集应该被加上,那么容斥系数等于莫比乌斯函数值。又由于是找的平方,那么一个数n的素因子的平方不大于n,也就是说该因子不大于根号下n,那么枚举素因子只要找到根号下n即可,找多了没有意义。然后就是根据k的值去确定这个n的值应该有多大。10^9个这样的数,则需要大约三十个素数的相互组合,也就是C(30,i) (i从0到31)的和为2^30次方,而由前三十个素数相乘的结果为7581744426003940878,但是实际上没有这么大,因为上面的情况并没有考虑素数的个数,如果把素数的个数算上的话, 2e10/ln(2e10) 约为8e8, 且2e5以内的素数个数约为16000,而C(16000,2)约为2e8 ,其实用不了这么大。那么上限可以取 1e10。
然后二分枚举这个要找的数据,判断这个数据之前有多少个符合条件数来划分区间。
#include <bits/stdc++.h>
#define sd(a) scanf("%d", &(a))
#define sdd(a) scanf("%lld", &(a))
#define pd(a) printf("%d\n", a)
#define pdd(a) printf("%lld\n", a)
#define int long long
#define ll long long
#define iint __int128
using namespace std;
template <class cl>
void read(cl &x)
{
x = 0;
int f = 0;
char ch;
ch = getchar();
while (!isdigit(ch))
{
f = f | (ch == '-'), ch = getchar();
}
while (isdigit(ch))
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x = f ? -x : x;
return;
}
template <class cl>
void put(cl x)
{
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
put(x / 10);
putchar(x % 10 + '0');
return;
}
const int N = 1e5 + 10;
ll fmul(ll a, ll b, ll p)
{
return (a * b - (ll)((long double)a * b / p) * p + p) % p;
}
int g[N+10];
int cnt = 0;
int prime[N+10];
int mu[N+10];
void getmu()
{
mu[1] = 1;
for (int i = 2; i <= N; i++)
{
if (g[i] == 0)
{
prime[++cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && i <=N / prime[j]; j++)
{
g[i * prime[j]] = 1;
if (i % prime[j] == 0)
{
break;
}
mu[i * prime[j]] = -mu[i];
}
}
}
int k;
int jud(int mid)
{
int res=0;
for(int i=1;i<=mid/i;i++)
{
if(mu[i]==0) continue;
res=res+mu[i]*(mid/(i*i));
}
return res;
}
signed main()
{
getmu();
// cout<<mu[6]<<endl;
int t;
read(t);
//int k;
while(t--)
{
read(k);
int l=k;
int r=k*2;
int mid;
int ans;
while(l<r)
{
mid=(l+r)/2;
// mid与(mid+mid+1)/2会相差一,mid符合条件但是mid+1 不符合条件,所以mid是要找的那个数字。
if(jud(mid)>=k)
{
r=mid;
}
else
{
l=mid+1;
}
}
put(r);
puts("");
}
return 0;
}
这个东西如果数据量为1e6的话,那么可以直接线性筛把这个函数的表求出来,然后再两次前缀和,但是,数据量大的吓人,这样肯定是不行,直接硬来不行,考虑用数学简化。
然后可以发现这道题和上一道题的套路差不多,但是这道题目中的集合不是集合中元素的个数,而是求集合中元素的平方和和立方和,需要用到平方和与立方和公式,容斥的方法和上题一样。然后还有一个小问题是数据过大,可以用__int128,或者是a/b%p=a%(b*p)/p+快速乘。
#include <bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;
template <class cl>
void read(cl &x)
{
x = 0;
int f = 0;
char ch;
ch = getchar();
while (!isdigit(ch))
{
f = f | (ch == '-'), ch = getchar();
}
while (isdigit(ch))
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x = f ? -x : x;
return;
}
template <class cl>
void put(cl x)
{
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
put(x / 10);
putchar(x % 10 + '0');
return;
}
const int N = 1e5 + 10;
ll fmul(ll a, ll b, ll p)
{
return (a * b - (ll)((long double)a * b / p) * p + p) % p;
}
int g[N+10];
int cnt = 0;
int prime[N+10];
int mu[N+10];
void getmu()
{
mu[1] = 1;
for (int i = 2; i <= N; i++)
{
if (g[i] == 0)
{
prime[++cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && i <=N / prime[j]; j++)
{
g[i * prime[j]] = 1;
if (i % prime[j] == 0)
{
break;
}
mu[i * prime[j]] = -mu[i];
}
}
}
signed main()
{
//cout<<fmul(1e10,1e10,6);
//cout<<fmul(1000,1000,7);
getmu();
//cout<<mu[5]<<endl;
//ios::sync_with_stdio(0);
//cin.tie(0);
// cout.tie(0);
int n, p;
// read(n);
// read(p);
while (~scanf("%lld%lld",&n,&p))
{
int res1 = 0;
int tt;
for (int i = 1; i * i <= n; i++)
{
tt = 0;
int zz=n/(i*i);
tt=fmul(fmul(zz,zz+1,6*p),2*zz+1,6*p);
tt=tt/6;
tt = tt * mu[i];
tt = fmul(tt, i * i, p);
tt = fmul(tt, i * i, p);
res1 = (res1 + tt) % p;
}
res1 = fmul(n + 1, res1, p);
int res2 = 0;
for (int i = 1; i * i <= n; i++)
{
tt = 0;
int zz=n/(i*i);
tt=fmul(fmul(zz,zz+1,4*p),fmul(zz,zz+1,4*p),4*p);
tt=tt/4;
tt = tt * mu[i];
tt = fmul(tt, i * i * i, p);
tt = fmul(tt, i * i * i, p);
res2 = (res2 + tt) % p;
}
int ans = 0;
ans = (res1 - res2 + p) % p;
printf("%lld\n",ans);
}
return 0;
}