题意:给定整数n和整数r,在1、2、3、4、5…….的序列中删掉可以开2次方的数,3次方的数,4次方的数,…….r次方的数,剩下的数称为Y序列,求Y序列中第n个数是多少 。
下面来自:https://blog.csdn.net/yanghuaqings/article/details/47006109
例如:n=15,r=5
正整数数列:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ……
当b=2时,去除的数:1 4 9 16 25 36 ……
当b=3时,去除的数:1 8 27 64 125 216 ……
当b=4时,去除的数:1 16 81 256 ……
当b=5时,去除的数:1 32 243 1024 ……
思路:假设我们知道了cal(x)表示包括x在内的x之前这个序列有多少个数,这个要容斥
x^(1/prime)可以算出1到x中有多少prime的次方数。二分超时,必须要用迭代(不超时,还得再研究研究)。
这个迭代的方法是我第一次见,很巧妙,我还得研究研究,另外容斥的奇减偶加的构造方法也是很有趣,也让我长见识了。
具体看代码解释吧。
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<stack>
#include<math.h>
#include<string>
#include<vector>
#include<cstdio>
#include<time.h>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
vector<int>q;
int prime[100]={0,-2,-3,-5,-7,-11,-13,-17,-19,-23,-29,-31,-37,-41,-43,-47,-53,-59,-61,-67};
void init(int r)
{
q.clear();
for(int i=1;abs(prime[i])<=r;i++)
{
int w=q.size();
for(int j=0;j<w;j++)
{
if(abs(prime[i]*q[j])<=62)
{
q.push_back(prime[i]*q[j]);//可以发现,这里的数已经符合了奇减偶加的条件且为下面预处理做好了准备
}
}
q.push_back(prime[i]);
}
}
ll cal(ll x)
{
if(x==1) return 0;
ll ans=x;
for(int i=0;i<q.size();i++)
{
ll temp=(ll)pow(x+0.5,1.0/abs(q[i]))-1;//注意控制精度,这里就是算出1到x右多少q[i]的次方数
if(q[i]<0) ans-=temp;
else ans+=temp;
}
return ans-1;
}
ll solve(ll n,int r)
{
init(r);
ll ans=n;
while(1)//迭代,每一次值加最小的可能值n-temp
{
ll temp=cal(ans);
if(temp==n) break;
ans=ans+n-temp;
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int r;
ll n;
scanf("%lld%d",&n,&r);
cout<<solve(n,r)<<endl;
}
}