Hdu 5297 Y sequence

题意:给定整数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;
    }
}
 

猜你喜欢

转载自blog.csdn.net/snayf/article/details/81106328