[题解][LuoguP4318]完全平方数

[题解]完全平方数

一.前言

​ 这题是洛谷的P4318来着。还有,好久没写题解了真是手生。听鹿乃的one,two专辑打代码真愉快

二.题意简述

​ 这个题意已经明显的不能再明显了,就是让你求第 k 个无完全平方因子的数

三.思路

​ (前面说了两个大点的废话来着)这道题康康数据范围,1e9 棘手。首先看到没有完全平方数,很显然的想到了莫比乌斯函数,(又是求个数还真是友好),于是尝试着将题目要求的东西用式子写出来。

​ 欸写不出来,神奇。这种求第k个以前都是一个个去枚举来着?但是范围决定了不好枚举,那就考虑考虑二分?那么再看一看,如果定义 \(f(x)\)“小于等于x的无完全平方数的个数”,很明显,这是一个单调递增的东西,可以二分。于是程序的大致模样就出来了。

​ 现在就差一个 \(f\) 函数的写法。先考虑一手能不能写成式子。

\[f(x)=\sum_{i=1}^{x}\mu^2(i)\\ 或者\;x-\sum\limits_{i=1}^{x}[\mu(i)+1=1] \]

上面那一个杜教筛筛不来,下面那一个反演演不出(也许可以顺着这个思路往下做?反正我是做不来。)

​ 那么康康其他方法,em,容斥吧?于是感性的想到了

\(f(x)=x-\)为一个素数的平方的倍数的个数+为两个素数的积的平方的倍数的个数-为三个素数乘积的平方的倍数的数+...

从语句成分来看,答案由这些部分组成: “x的倍数的个数” , “k个素数的积平方”,“+或-”。

  • 倍数的个数:直接向下取整除就好了嗷,小学数学。
  • k个素数的积的平方:枚举肯定不是枚举 k,而是枚举 k 个素数的积。
  • 系数:一个素数是 - ,两个素数是 + ,三个是 - ,又要根据这个数来判断由几个质数组成,所以赤裸裸的莫比乌斯函数。

啊。上面基本上都分析清楚了,整合一下就是

\[f(x)=\sum\limits_{i=1}^{i<=\sqrt{x}}\mu(i)\lfloor\frac{x}{i^2}\rfloor \]

(枚举范围看看式子就知道了啊,不要为 1e9 操心)

CODE

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define m_for(i,a,b) for(int i=a;i<=b;++i)
const int MAXN=1e5+5;
ll n,m;
int prime[MAXN],mu[MAXN],tot,sum;
bool vis[MAXN],ans[MAXN];
void m_p(int x){
	mu[1]=1;
	m_for(i,2,x){
		if(!vis[i])prime[++tot]=i,mu[i]=-1;
		for(int j=1;j<=tot&&i*prime[j]<=x;++j){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)break;
			mu[i*prime[j]]=-mu[i];
		}
	}
}
ll solve(ll x){
	ll res=0;
	for(int i=1;i*i<=x;++i)res+=1LL*x/i/i*mu[i];
	return res;
}
ll l,r,mid,k,t;
int main(){
m_p(MAXN);
cin>>t;
while(t--){
	cin>>k;
	r=k<<1,l=k;
	while(l<r){
		mid=(l+r)>>1;
		if(solve(mid)<k)l=mid+1;
		else r=mid;
	}
	cout<<l<<endl;
}
return 0;
}
posted @ 2020-02-15 16:22  clockwhite  阅读(175)  评论(0编辑  收藏  举报